From 70b3d0efa60b9c63df371b7d99c5f0ed926ffb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Kohlgr=C3=BCber?= Date: Tue, 12 Mar 2019 13:38:46 +0100 Subject: [PATCH 001/524] add syntax-tree-patterns RFC --- rfcs/0001-syntax-tree-patterns.md | 797 ++++++++++++++++++++++++++++++ 1 file changed, 797 insertions(+) create mode 100644 rfcs/0001-syntax-tree-patterns.md diff --git a/rfcs/0001-syntax-tree-patterns.md b/rfcs/0001-syntax-tree-patterns.md new file mode 100644 index 000000000000..8d285b2ef44c --- /dev/null +++ b/rfcs/0001-syntax-tree-patterns.md @@ -0,0 +1,797 @@ +- Feature Name: syntax-tree-patterns +- Start Date: 2019-03-12 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +> Note: This project is part of my Master's Thesis (supervised by [@oli-obk](https://github.com/oli-obk)) + +# Summary +[summary]: #summary + +Introduce a domain-specific language (similar to regular expressions) that allows to describe lints using *syntax tree patterns*. + + +# Motivation +[motivation]: #motivation + + +Finding parts of a syntax tree (AST, HIR, ...) that have certain properties (e.g. "*an if that has a block as its condition*") is a major task when writing lints. For non-trivial lints, it often requires nested pattern matching of AST / HIR nodes. For example, testing that an expression is a boolean literal requires the following checks: + +``` +if let ast::ExprKind::Lit(lit) = &expr.node { + if let ast::LitKind::Bool(_) = &lit.node { + ... + } +} +``` + +Writing this kind of matching code quickly becomes a complex task and the resulting code is often hard to comprehend. The code below shows a simplified version of the pattern matching required by the `collapsible_if` lint: + +``` +// simplified version of the collapsible_if lint +if let ast::ExprKind::If(check, then, None) = &expr.node { + if then.stmts.len() == 1 { + if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node { + if let ast::ExprKind::If(check_inner, content, None) = &inner.node { + ... + } + } + } +} +``` + +The `if_chain` macro can improve readability by flattening the nested if statements, but the resulting code is still quite hard to read: + +``` +// simplified version of the collapsible_if lint +if_chain! { + if let ast::ExprKind::If(check, then, None) = &expr.node; + if then.stmts.len() == 1; + if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node; + if let ast::ExprKind::If(check_inner, content, None) = &inner.node; + then { + ... + } +} +``` + +The code above matches if expressions that contain only another if expression (where both ifs don't have an else branch). While it's easy to explain what the lint does, it's hard to see that from looking at the code samples above. + +Following the motivation above, the first goal this RFC is to **simplify writing and reading lints**. + +The second part of the motivation is clippy's dependence on unstable compiler-internal data structures. Clippy lints are currently written against the compiler's AST / HIR which means that even small changes in these data structures might break a lot of lints. The second goal of this RFC is to **make lints independant of the compiler's AST / HIR data structures**. + +# Approach + +A lot of complexity in writing lints currently seems to come from having to manually implement the matching logic (see code samples above). It's an imparative style that describes *how* to match a syntax tree node instead of specifying *what* should be matched against declaratively. In other areas, it's common to use declarative patterns to describe desired information and let the implementation do the actual matching. A well-known example of this approach are [regular expressions](https://en.wikipedia.org/wiki/Regular_expression). Instead of writing code that detects certain character sequences, one can describe a search pattern using a domain-specific language and search for matches using that pattern. The advantage of using a declarative domain-specific language is that its limited domain (e.g. matching character sequences in the case of regular expressions) allows to express entities in that domain in a very natural and expressive way. + +While regular expressions are very useful when searching for patterns in flat character sequences, they cannot easily be applied to hierarchical data structures like syntax trees. This RFC therefore proposes a pattern matching system that is inspired by regular expressions and designed for hierarchical syntax trees. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +This proposal adds a `pattern!` macro that can be used to specify a syntax tree pattern to search for. A simple pattern is shown below: + +``` +pattern!{ + my_pattern: Expr = + Lit(Bool(false)) +} +``` + +This macro call defines a pattern named `my_pattern` that can be matched against an `Expr` syntax tree node. The actual pattern (`Lit(Bool(false))` in this case) defines which syntax trees should match the pattern. This pattern matches expressions that are boolean literals with value `false`. + +The pattern can then be used to implement lints in the following way: + +``` +... + +impl EarlyLintPass for MyAwesomeLint { + fn check_expr(&mut self, cx: &EarlyContext, expr: &syntax::ast::Expr) { + + if my_pattern(expr).is_some() { + cx.span_lint( + MY_AWESOME_LINT, + expr.span, + "This is a match for a simple pattern. Well done!", + ); + } + + } +} +``` + +The `pattern!` macro call expands to a function `my_pattern` that expects a syntax tree expression as its argument and returns an `Option` that indicates whether the pattern matched. + +> Note: The result type is explained in more detail in [a later section](#the-result-type). For now, it's enough to know that the result is `Some` if the pattern matched and `None` otherwise. + +## Pattern syntax + +The following examples demonstate the pattern syntax: + + +#### Any (`_`) + +The simplest pattern is the any pattern. It matches anything and is therefore similar to regex's `*`. + +``` +pattern!{ + // matches any expression + my_pattern: Expr = + _ +} +``` + +#### Node (`()`) + +Nodes are used to match a specific variant of an AST node. A node has a name and a number of arguments that depends on the node type. For example, the `Lit` node has a single argument that describes the type of the literal. As another example, the `If` node has three arguments describing the if's condition, then block and else block. + +``` +pattern!{ + // matches any expression that is a literal + my_pattern: Expr = + Lit(_) +} + +pattern!{ + // matches any expression that is a boolean literal + my_pattern: Expr = + Lit(Bool(_)) +} + +pattern!{ + // matches if expressions that have a boolean literal in their condition + // Note: The `_?` syntax here means that the else branch is optional and can be anything. + // This is discussed in more detail in the section `Repetition`. + my_pattern: Expr = + If( Lit(Bool(_)) , _, _?) +} +``` + + +#### Literal (``) + +A pattern can also contain Rust literals. These literals match themselves. + +``` +pattern!{ + // matches the boolean literal false + my_pattern: Expr = + Lit(Bool(false)) +} + +pattern!{ + // matches the character literal 'x' + my_pattern: Expr = + Lit(Char('x')) +} +``` + +#### Alternations (`a | b`) + +``` +pattern!{ + // matches if the literal is a boolean or integer literal + my_pattern: Lit = + Bool(_) | Int(_) +} + +pattern!{ + // matches if the expression is a char literal with value 'x' or 'y' + my_pattern: Expr = + Lit( Char('x' | 'y') ) +} +``` + +#### Empty (`()`) + +The empty pattern represents an empty sequence or the `None` variant of an optional. + +``` +pattern!{ + // matches if the expression is an empty array + my_pattern: Expr = + Array( () ) +} + +pattern!{ + // matches if expressions that don't have an else clause + my_pattern: Expr = + If(_, _, ()) +} +``` + +#### Sequence (` `) + +``` +pattern!{ + // matches the array [true, false] + my_pattern: Expr = + Array( Lit(Bool(true)) Lit(Bool(false)) ) +} +``` + +#### Repetition (`*`, `+`, `?`, `{n}`, `{n,m}`, `{n,}`) + +Elements may be repeated. The syntax for specifying repetitions is identical to [regex's syntax](https://docs.rs/regex/1.1.2/regex/#repetitions). + +``` +pattern!{ + // matches arrays that contain 2 'x's as their last or second-last elements + // Examples: + // ['x', 'x'] match + // ['x', 'x', 'y'] match + // ['a', 'b', 'c', 'x', 'x', 'y'] match + // ['x', 'x', 'y', 'z'] no match + my_pattern: Expr = + Array( _* Lit(Char('x')){2} _? ) +} + +pattern!{ + // matches if expressions that **may or may not** have an else block + // Attn: `If(_, _, _)` matches only ifs that **have** an else block + // + // | if with else block | if witout else block + // If(_, _, _) | match | no match + // If(_, _, _?) | match | match + // If(_, _, ()) | no match | match + my_pattern: Expr = + If(_, _, _?) +} +``` + +#### Named submatch (`#`) + +``` +pattern!{ + // matches character literals and gives the literal the name foo + my_pattern: Expr = + Lit(Char(_)#foo) +} + +pattern!{ + // matches character literals and gives the char the name bar + my_pattern: Expr = + Lit(Char(_#bar)) +} + +pattern!{ + // matches character literals and gives the expression the name baz + my_pattern: Expr = + Lit(Char(_))#baz +} +``` + +The reason for using named submatches is described in the section [The result type](#the-result-type). + +### Summary + +The following table gives an summary of the pattern syntax: + +| Syntax | Concept | Examples | +|-------------------------|------------------|--------------------------------------------| +|`_` | Any | `_` | +|`()` | Node | `Lit(Bool(true))`, `If(_, _, _)` | +|`` | Literal | `'x'`, `false`, `101` | +|` \| ` | Alternation | `Char(_) \| Bool(_)` | +|`()` | Empty | `Array( () )` | +|` ` | Sequence | `Tuple( Lit(Bool(_)) Lit(Int(_)) Lit(_) )` | +|`*`
`
+`
`
?`
`
{n}`
`
{n,m}`
`
{n,}` | Repetition





| `Array( _* )`,
`Block( Semi(_)+ )`,
`If(_, _, Block(_)?)`,
`Array( Lit(_){10} )`,
`Lit(_){5,10}`,
`Lit(Bool(_)){10,}` | +|`
#` | Named submatch | `Lit(Int(_))#foo` `Lit(Int(_#bar))` | + + +## The result type +[the-result-type]: #the-result-type + +A lot of lints require checks that go beyond what the pattern syntax described above can express. For example, a lint might want to check whether a node was created as part of a macro expansion or whether there's no comment above a node. Another example would be a lint that wants to match two nodes that have the same value (as needed by lints like `almost_swapped`). Instead of allowing users to write these checks into the pattern directly (which might make patterns hard to read), the proposed solution allows users to assign names to parts of a pattern expression. When matching a pattern against a syntax tree node, the return value will contain references to all nodes that were matched by these named subpatterns. This is similar to capture groups in regular expressions. + +For example, given the following pattern + +``` +pattern!{ + // matches character literals + my_pattern: Expr = + Lit(Char(_#val_inner)#val)#val_outer +} +``` + +one could get references to the nodes that matched the subpatterns in the following way: + +``` +... +fn check_expr(expr: &syntax::ast::Expr) { + if let Some(result) = my_pattern(expr) { + result.val_inner // type: &char + result.val // type: &syntax::ast::Lit + result.val_outer // type: &syntax::ast::Expr + } +} +``` + +The types in the `result` struct depend on the pattern. For example, the following pattern + +``` +pattern!{ + // matches arrays of character literals + my_pattern_seq: Expr = + Array( Lit(_)*#foo ) +} +``` + +matches arrays that consist of any number of literal expressions. Because those expressions are named `foo`, the result struct contains a `foo` attribute which is a vector of expressions: + +``` +... +if let Some(result) = my_pattern_seq(expr) { + result.foo // type: Vec<&syntax::ast::Expr> +} +``` + +Another result type occurs when a name is only defined in one branch of an alternation: + +``` +pattern!{ + // matches if expression is a boolean or integer literal + my_pattern_alt: Expr = + Lit( Bool(_#bar) | Int(_) ) +} +``` + +In the pattern above, the `bar` name is only defined if the pattern matches a boolean literal. If it matches an integer literal, the name isn't set. To account fot this, the result struct's `bar` attribute is an option type: + +``` +... +if let Some(result) = my_pattern_alt(expr) { + result.bar // type: Option<&bool> +} +``` + +It's also possible to use a name in multiple alternation branches if they have compatible types: + +``` +pattern!{ + // matches if expression is a boolean or integer literal + my_pattern_mult: Expr = + Lit(_#baz) | Array( Lit(_#baz) ) +} +... +if let Some(result) = my_pattern_mult(expr) { + result.baz // type: &syntax::ast::Lit +} +``` + +Named submatches are a **flat** namespace and this is intended. In the example above, two different sub-structures are assigned to a flat name. I expect that for most lints, a flat namespace is sufficient and easier to work with than a hierarchical one. + +#### Two stages + +Using named subpatterns, users can write lints in two stages. First, a coarse selection of possible matches is produced by the pattern syntax. In the second stage, the named subpattern references can be used to do additional tests like asserting that a node hasn't been created as part of a macro expansion. + +## Implementing clippy lints using patterns + +As a "real-world" example, I re-implemented the `collapsible_if` lint using patterns. The code can be found [here](https://github.com/fkohlgrueber/rust-clippy-pattern/blob/039b07ecccaf96d6aa7504f5126720d2c9cceddd/clippy_lints/src/collapsible_if.rs#L88-L163). The pattern-based version passes all test cases that were written for `collapsible_if`. + + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +## Overview + +The following diagram shows the dependencies between the main parts of the proposed solution: + +``` + Pattern syntax + | + | parsing / lowering + v + PatternTree + ^ + | + | + IsMatch trait + | + | + +---------------+-----------+---------+ + | | | | + v v v v + syntax::ast rustc::hir syn ... +``` + +The pattern syntax described in the previous section is parsed / lowered into the so-called *PatternTree* data structure that represents a valid syntax tree pattern. Matching a *PatternTree* against an actual syntax tree (e.g. rust ast / hir or the syn ast, ...) is done using the *IsMatch* trait. + +The *PatternTree* and the *IsMatch* trait are introduced in more detail in the following sections. + +## PatternTree + +The core data structure of this RFC is the **PatternTree**. + +It's a data structure similar to rust's AST / HIR, but with the following differences: + +- The PatternTree doesn't contain parsing information like `Span`s +- The PatternTree can represent alternatives, sequences and optionals + +The code below shows a simplified version of the current PatternTree: + +> Note: The current implementation can be found [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/pattern_tree.rs#L50-L96). + + +``` +pub enum Expr { + Lit(Alt), + Array(Seq), + Block_(Alt), + If(Alt, Alt, Opt), + IfLet( + Alt, + Opt, + ), +} + +pub enum Lit { + Char(Alt), + Bool(Alt), + Int(Alt), +} + +pub enum Stmt { + Expr(Alt), + Semi(Alt), +} + +pub enum BlockType { + Block(Seq), +} +``` + +The `Alt`, `Seq` and `Opt` structs look like these: + +> Note: The current implementation can be found [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60). + +``` +pub enum Alt { + Any, + Elmt(Box), + Alt(Box, Box), + Named(Box, ...) +} + +pub enum Opt { + Any, // anything, but not None + Elmt(Box), + None, + Alt(Box, Box), + Named(Box, ...) +} + +pub enum Seq { + Any, + Empty, + Elmt(Box), + Repeat(Box, RepeatRange), + Seq(Box, Box), + Alt(Box, Box), + Named(Box, ...) +} + +pub struct RepeatRange { + pub start: usize, + pub end: Option // exclusive +} +``` + +## Parsing / Lowering + +The input of a `pattern!` macro call is parsed into a `ParseTree` first and then lowered to a `PatternTree`. + +Valid patterns depend on the *PatternTree* definitions. For example, the pattern `Lit(Bool(_)*)` isn't valid because the parameter type of the `Lit` variant of the `Expr` enum is `Any` and therefore doesn't support repetition (`*`). As another example, `Array( Lit(_)* )` is a valid pattern because the parameter of `Array` is of type `Seq` which allows sequences and repetitions. + +> Note: names in the pattern syntax correspond to *PatternTree* enum **variants**. For example, the `Lit` in the pattern above refers to the `Lit` variant of the `Expr` enum (`Expr::Lit`), not the `Lit` enum. + +## The IsMatch Trait + +The pattern syntax and the *PatternTree* are independant of specific syntax tree implementations (rust ast / hir, syn, ...). When looking at the different pattern examples in the previous sections, it can be seen that the patterns don't contain any information specific to a certain syntax tree implementation. In contrast, clippy lints currently match against ast / hir syntax tree nodes and therefore directly depend on their implementation. + +The connection between the *PatternTree* and specific syntax tree implementations is the `IsMatch` trait. It defines how to match *PatternTree* nodes against specific syntax tree nodes. A simplified implementation of the `IsMatch` trait is shown below: + +``` +pub trait IsMatch { + fn is_match(&self, other: &'o O) -> bool; +} +``` + +This trait needs to be implemented on each enum of the *PatternTree* (for the corresponding syntax tree types). For example, the `IsMatch` implementation for matching `ast::LitKind` against the *PatternTree's* `Lit` enum might look like this: + +``` +impl IsMatch for Lit { + fn is_match(&self, other: &ast::LitKind) -> bool { + match (self, other) { + (Lit::Char(i), ast::LitKind::Char(j)) => i.is_match(j), + (Lit::Bool(i), ast::LitKind::Bool(j)) => i.is_match(j), + (Lit::Int(i), ast::LitKind::Int(j, _)) => i.is_match(j), + _ => false, + } + } +} +``` + +All `IsMatch` implementations for matching the current *PatternTree* against `syntax::ast` can be found [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/ast_match.rs). + + +# Drawbacks +[drawbacks]: #drawbacks + +#### Performance + +The pattern matching code is currently not optimized for performance, so it might be slower than hand-written matching code. +Additionally, the two-stage approach (matching against the coarse pattern first and checking for additional properties later) might be slower than the current practice of checking for structure and additional properties in one pass. For example, the following lint + +``` +pattern!{ + pat_if_without_else: Expr = + If( + _, + Block( + Expr( If(_, _, ())#inner ) + | Semi( If(_, _, ())#inner ) + )#then, + () + ) +} +... +fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if let Some(result) = pat_if_without_else(expr) { + if !block_starts_with_comment(cx, result.then) { + ... + } +} +``` + +first matches against the pattern and then checks that the `then` block doesn't start with a comment. Using clippy's current approach, it's possible to check for these conditions earlier: + +``` +fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if_chain! { + if let ast::ExprKind::If(ref check, ref then, None) = expr.node; + if !block_starts_with_comment(cx, then); + if let Some(inner) = expr_block(then); + if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node; + then { + ... + } + } +} +``` + +Whether or not this causes performance regressions depends on actual patterns. If it turns out to be a problem, the pattern matching algorithms could be extended to allow "early filtering" (see the [Early Filtering](#early-filtering) section in Future Possibilities). + +That being said, I don't see any conceptual limitations regarding pattern matching performance. + +#### Applicability + +Even though I'd expect that a lot of lints can be written using the proposed pattern syntax, it's unlikely that all lints can be expressed using patterns. I suspect that there will still be lints that need to be implemented by writing custom pattern matching code. This would lead to mix within clippy's codebase where some lints are implemented using patterns and others aren't. This inconsistency might be considered a drawback. + + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +Specifying lints using syntax tree patterns has a couple of advantages compared to the current approach of manually writing matching code. First, syntax tree patterns allow users to describe patterns in a simple and expressive way. This makes it easier to write new lints for both novices and experts and also makes reading / modifying existing lints simpler. + +Another advantage is that lints are independent of specific syntax tree implementations (e.g. AST / HIR, ...). When these syntax tree implementations change, only the `IsMatch` trait implementations need to be adapted and existing lints can remain unchanged. This also means that if the `IsMatch` trait implementations were integrated into the compiler, updating the `IsMatch` implementations would be required for the compiler to compile successfully. This could reduce the number of times clippy breaks because of changes in the compiler. Another advantage of the pattern's independence is that converting an `EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole pattern matching code. In fact, the pattern might work just fine without any adaptions. + + + +## Alternatives + +### Rust-like pattern syntax + +The proposed pattern syntax requires users to know the structure of the `PatternTree` (which is very similar to the AST's / HIR's structure) and also the pattern syntax. An alternative would be to introduce a pattern syntax that is similar to actual Rust syntax (probably like the `quote!` macro). For example, a pattern that matches `if` expressions that have `false` in their condition could look like this: + +``` +if false { + #[*] +} +``` + +#### Problems + +Extending Rust syntax (which is quite complex by itself) with additional syntax needed for specifying patterns (alternations, sequences, repetisions, named submatches, ...) might become difficult to read and really hard to parse properly. + +For example, a pattern that matches a binary operation that has `0` on both sides might look like this: + +``` +0 #[*:BinOpKind] 0 +``` + +Now consider this slightly more complex example: + +``` +1 + 0 #[*:BinOpKind] 0 +``` + +The parser would need to know the precedence of `#[*:BinOpKind]` because it affects the structure of the resulting AST. `1 + 0 + 0` is parsed as `(1 + 0) + 0` while `1 + 0 * 0` is parsed as `1 + (0 * 0)`. Since the pattern could be any `BinOpKind`, the precedence cannot be known in advance. + +Another example of a problem would be named submatches. Take a look at this pattern: + +``` +fn test() { + 1 #foo +} +``` + +Which node is `#foo` referring to? `int`, `ast::Lit`, `ast::Expr`, `ast::Stmt`? Naming subpatterns in a rust-like syntax is difficult because a lot of AST nodes don't have a syntactic element that can be used to put the name tag on. In these situations, the only sensible option would be to assign the name tag to the outermost node (`ast::Stmt` in the example above), because the information of all child nodes can be retrieved through the outermost node. The problem with this then would be that accessing inner nodes (like `ast::Lit`) would again require manual pattern matching. + +In general, Rust syntax contains a lot of code structure implicitly. This structure is reconstructed during parsing (e.g. binary operations are reconstructed using operator precedence and left-to-right) and is one of the reasons why parsing is a complex task. The advantage of this approach is that writing code is simpler for users. + +When writing *syntax tree patterns*, each element of the hierarchy might have alternatives, repetitions, etc.. Respecting that while still allowing human-friendly syntax that contains structure implicitly seems to be really complex, if not impossible. + +Developing such a syntax would also require to maintain a custom parser that is at least as complex as the Rust parser itself. Additionally, future changes in the Rust syntax might be incompatible with such a syntax. + +In summary, I think that developing such a syntax would introduce a lot of complexity to solve a relatively minor problem. + +The issue of users not knowing about the *PatternTree* structure could be solved by a tool that, given a rust program, generates a pattern that matches only this program (similar to the clippy author lint). + +For some simple cases (like the first example above), it might be possible to successfully mix Rust and pattern syntax. This space could be further explored in a future extension. + +# Prior art +[prior-art]: #prior-art + +The pattern syntax is heavily inspired by regular expressions (repetitions, alternatives, sequences, ...). + +From what I've seen until now, other linters also implement lints that directly work on syntax tree data structures, just like clippy does currently. I would therefore consider the pattern syntax to be *new*, but please correct me if I'm wrong. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +#### How to handle multiple matches? + +When matching a syntax tree node against a pattern, there are possibly multiple ways in which the pattern can be matched. A simple example of this would be the following pattern: + +``` +pattern!{ + my_pattern: Expr = + Array( _* Lit(_)+#literals) +} +``` + +This pattern matches arrays that end with at least one literal. Now given the array `[x, 1, 2]`, should `1` be matched as part of the `_*` or the `Lit(_)+` part of the pattern? The difference is important because the named submatch `#literals` would contain 1 or 2 elements depending how the pattern is matched. In regular expressions, this problem is solved by matching "greedy" by default and "non-greedy" optionally. + +I haven't looked much into this yet because I don't know how relevant it is for most lints. The current implementation simply returns the first match it finds. + +# Future possibilities +[future-possibilities]: #future-possibilities + +#### Implement rest of Rust Syntax + +The current project only implements a small part of the Rust syntax. In the future, this should incrementally be extended to more syntax to allow implementing more lints. Implementing more of the Rust syntax requires extending the `PatternTree` and `IsMatch` implementations, but should be relatively straight-forward. + +#### Early filtering + +As described in the *Drawbacks/Performance* section, allowing additional checks during the pattern matching might be beneficial. + +The pattern below shows how this could look like: + +``` +pattern!{ + pat_if_without_else: Expr = + If( + _, + Block( + Expr( If(_, _, ())#inner ) + | Semi( If(_, _, ())#inner ) + )#then, + () + ) + where + !in_macro(#then.span); +} +``` + +The difference compared to the currently proposed two-stage filtering is that using early filtering, the condition (`!in_macro(#then.span)` in this case) would be evaluated as soon as the `Block(_)#then` was matched. + +Another idea in this area would be to introduce a syntax for backreferences. They could be used to require that multiple parts of a pattern should match the same value. For example, the `assign_op_pattern` lint that searches for `a = a op b` and recommends changing it to `a op= b` requires that both occurrances of `a` are the same. Using `=#...` as syntax for backreferences, the lint could be implemented like this: + +``` +pattern!{ + assign_op_pattern: Expr = + Assign(_#target, Binary(_, =#target, _) +} +``` + +#### Match descendant + +A lot of lints currently implement custom visitors that check whether any subtree (which might not be a direct descendant) of the current node matches some properties. This cannot be expressed with the proposed pattern syntax. Extending the pattern syntax to allow patterns like "a function that contains at least two return statements" could be a practical addition. + +#### Negation operator for alternatives + +For patterns like "a literal that is not a boolean literal" one currently needs to list all alternatives except the boolean case. Introducing a negation operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three literal types are implemented). + +#### Functional composition + +Patterns currently don't have any concept of composition. This leads to repetitions within patterns. For example, one of the collapsible-if patterns currently has to be written like this: + +``` +pattern!{ + pat_if_else: Expr = + If( + _, + _, + Block_( + Block( + Expr((If(_, _, _?) | IfLet(_, _?))#else_) | + Semi((If(_, _, _?) | IfLet(_, _?))#else_) + )#block_inner + )#block + ) | + IfLet( + _, + Block_( + Block( + Expr((If(_, _, _?) | IfLet(_, _?))#else_) | + Semi((If(_, _, _?) | IfLet(_, _?))#else_) + )#block_inner + )#block + ) +} +``` + +If patterns supported defining functions of subpatterns, the code could be simplified as follows: + +``` +pattern!{ + fn expr_or_semi(expr: Expr) -> Stmt { + Expr(expr) | Semi(expr) + } + fn if_or_if_let(then: Block, else: Opt) -> Expr { + If(_, then, else) | IfLet(then, else) + } + pat_if_else: Expr = + if_or_if_let( + _, + Block_( + Block( + expr_or_semi( if_or_if_let(_, _?)#else_ ) + )#block_inner + )#block + ) +} +``` + +Additionally, common patterns like `expr_or_semi` could be shared between different lints. + +#### Clippy Pattern Author + +Another improvement could be to create a tool that, given some valid Rust syntax, generates a pattern that matches this syntax exactly. This would make starting to write a pattern easier. A user could take a look at the patterns generated for a couple of Rust code examples and use that information to write a pattern that matches all of them. + +This is similar to clippy's author lint. + +#### Supporting other syntaxes + +Most of the proposed system is language-agnostic. For example, the pattern syntax could also be used to describe patterns for other programming languages. + +In order to support other languages' syntaxes, one would need to implement another `PatternTree` that sufficiently describes the languages' AST and implement `IsMatch` for this `PatternTree` and the languages' AST. + +One aspect of this is that it would even be possible to write lints that work on the pattern syntax itself. For example, when writing the following pattern + + +``` +pattern!{ + my_pattern: Expr = + Array( Lit(Bool(false)) Lit(Bool(false)) ) +} +``` + +a lint that works on the pattern syntax's AST could suggest using this pattern instead: + +``` +pattern!{ + my_pattern: Expr = + Array( Lit(Bool(false)){2} ) +} +``` + +In the future, clippy could use this system to also provide lints for custom syntaxes like those found in macros. + +# Final Note + +This is the first RFC I've ever written and it might be a little rough around the edges. + +I'm looking forward to hearing your feedback and discussing the proposal. From a6444a69e27275c69e7287fe02ba0c88d554c445 Mon Sep 17 00:00:00 2001 From: Nahua Kang Date: Tue, 23 Aug 2022 19:05:57 +0200 Subject: [PATCH 002/524] Remove if_chain from equatable_if_let --- clippy_lints/src/equatable_if_let.rs | 59 +++++++++++++--------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index fdfb821ac789..ba615c8c1648 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -67,37 +66,33 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Let(let_expr) = expr.kind; - if unary_pattern(let_expr.pat); - let exp_ty = cx.typeck_results().expr_ty(let_expr.init); - let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); - if is_structural_partial_eq(cx, exp_ty, pat_ty); - then { - - let mut applicability = Applicability::MachineApplicable; - let pat_str = match let_expr.pat.kind { - PatKind::Struct(..) => format!( - "({})", - snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), - }; - span_lint_and_sugg( - cx, - EQUATABLE_IF_LET, - expr.span, - "this pattern matching can be expressed using equality", - "try", - format!( - "{} == {}", - snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - pat_str, - ), - applicability, - ); - } + if !in_external_macro(cx.sess(), expr.span) + && let ExprKind::Let(let_expr) = expr.kind + && unary_pattern(let_expr.pat) + && let exp_ty = cx.typeck_results().expr_ty(let_expr.init) + && let pat_ty = cx.typeck_results().pat_ty(let_expr.pat) + && is_structural_partial_eq(cx, exp_ty, pat_ty) { + let mut applicability = Applicability::MachineApplicable; + let pat_str = match let_expr.pat.kind { + PatKind::Struct(..) => format!( + "({})", + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), + }; + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using equality", + "try", + format!( + "{} == {}", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + pat_str, + ), + applicability, + ); } } } From 5ee1c24f28029162ad25614c092224cc94ba59ff Mon Sep 17 00:00:00 2001 From: Nahua Kang Date: Tue, 23 Aug 2022 19:46:04 +0200 Subject: [PATCH 003/524] Lint suggests matches macro if PartialEq trait is not implemented --- clippy_lints/src/equatable_if_let.rs | 64 +++++++++++++++++----------- tests/ui/equatable_if_let.rs | 7 +++ 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index ba615c8c1648..6f26195d105c 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -68,31 +68,47 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if !in_external_macro(cx.sess(), expr.span) && let ExprKind::Let(let_expr) = expr.kind - && unary_pattern(let_expr.pat) - && let exp_ty = cx.typeck_results().expr_ty(let_expr.init) - && let pat_ty = cx.typeck_results().pat_ty(let_expr.pat) - && is_structural_partial_eq(cx, exp_ty, pat_ty) { + && unary_pattern(let_expr.pat) { + let exp_ty = cx.typeck_results().expr_ty(let_expr.init); + let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); let mut applicability = Applicability::MachineApplicable; - let pat_str = match let_expr.pat.kind { - PatKind::Struct(..) => format!( - "({})", - snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), - _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), - }; - span_lint_and_sugg( - cx, - EQUATABLE_IF_LET, - expr.span, - "this pattern matching can be expressed using equality", - "try", - format!( - "{} == {}", - snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - pat_str, - ), - applicability, - ); + + if is_structural_partial_eq(cx, exp_ty, pat_ty) { + let pat_str = match let_expr.pat.kind { + PatKind::Struct(..) => format!( + "({})", + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(), + }; + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using equality", + "try", + format!( + "{} == {}", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + pat_str, + ), + applicability, + ); + } else { + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using `matches!`", + "try", + format!( + "matches!({}, {})", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + applicability, + ) + } } } } diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 8c467d14d2a9..c3626c081dd5 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -23,6 +23,11 @@ struct Struct { b: bool, } +struct NoPartialEqStruct { + a: i32, + b: bool, +} + enum NotPartialEq { A, B, @@ -47,6 +52,7 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h = NoPartialEqStruct { a: 2, b: false }; // true @@ -70,6 +76,7 @@ fn main() { if let NotStructuralEq::A = g {} if let Some(NotPartialEq::A) = Some(f) {} if let Some(NotStructuralEq::A) = Some(g) {} + if let NoPartialEqStruct { a: 2, b: false } = h {} macro_rules! m1 { (x) => { From 4eaadd622d21948c488a4952b3c65fd5b9d852dc Mon Sep 17 00:00:00 2001 From: Nahua Kang Date: Tue, 23 Aug 2022 19:50:34 +0200 Subject: [PATCH 004/524] Run cargo dev bless to update fixes & stderr --- clippy_lints/src/equatable_if_let.rs | 10 +++---- tests/ui/equatable_if_let.fixed | 11 ++++++-- tests/ui/equatable_if_let.stderr | 42 ++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 6f26195d105c..928128785f49 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -96,18 +96,18 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { ); } else { span_lint_and_sugg( - cx, + cx, EQUATABLE_IF_LET, expr.span, - "this pattern matching can be expressed using `matches!`", - "try", + "this pattern matching can be expressed using `matches!`", + "try", format!( "matches!({}, {})", snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, - ), + ), applicability, - ) + ); } } } diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 687efdada6e3..9af2ba962720 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -23,6 +23,11 @@ struct Struct { b: bool, } +struct NoPartialEqStruct { + a: i32, + b: bool, +} + enum NotPartialEq { A, B, @@ -47,6 +52,7 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h = NoPartialEqStruct { a: 2, b: false }; // true @@ -66,10 +72,11 @@ fn main() { if let Some(3 | 4) = c {} if let Struct { a, b: false } = d {} if let Struct { a: 2, b: x } = d {} - if let NotPartialEq::A = f {} + if matches!(f, NotPartialEq::A) {} if g == NotStructuralEq::A {} - if let Some(NotPartialEq::A) = Some(f) {} + if matches!(Some(f), Some(NotPartialEq::A)) {} if Some(g) == Some(NotStructuralEq::A) {} + if matches!(h, NoPartialEqStruct { a: 2, b: false }) {} macro_rules! m1 { (x) => { diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index 9c4c3cc3682e..40ca75b8da22 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -1,5 +1,5 @@ error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:53:8 + --> $DIR/equatable_if_let.rs:59:8 | LL | if let 2 = a {} | ^^^^^^^^^ help: try: `a == 2` @@ -7,64 +7,82 @@ LL | if let 2 = a {} = note: `-D clippy::equatable-if-let` implied by `-D warnings` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:54:8 + --> $DIR/equatable_if_let.rs:60:8 | LL | if let Ordering::Greater = a.cmp(&b) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:55:8 + --> $DIR/equatable_if_let.rs:61:8 | LL | if let Some(2) = c {} | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:56:8 + --> $DIR/equatable_if_let.rs:62:8 | LL | if let Struct { a: 2, b: false } = d {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:57:8 + --> $DIR/equatable_if_let.rs:63:8 | LL | if let Enum::TupleVariant(32, 64) = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:58:8 + --> $DIR/equatable_if_let.rs:64:8 | LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:59:8 + --> $DIR/equatable_if_let.rs:65:8 | LL | if let Enum::UnitVariant = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:60:8 + --> $DIR/equatable_if_let.rs:66:8 | LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })` +error: this pattern matching can be expressed using `matches!` + --> $DIR/equatable_if_let.rs:75:8 + | +LL | if let NotPartialEq::A = f {} + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)` + error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:70:8 + --> $DIR/equatable_if_let.rs:76:8 | LL | if let NotStructuralEq::A = g {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A` +error: this pattern matching can be expressed using `matches!` + --> $DIR/equatable_if_let.rs:77:8 + | +LL | if let Some(NotPartialEq::A) = Some(f) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))` + error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:72:8 + --> $DIR/equatable_if_let.rs:78:8 | LL | if let Some(NotStructuralEq::A) = Some(g) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` -error: this pattern matching can be expressed using equality +error: this pattern matching can be expressed using `matches!` --> $DIR/equatable_if_let.rs:79:8 | +LL | if let NoPartialEqStruct { a: 2, b: false } = h {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:86:8 + | LL | if let m1!(x) = "abc" { | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)` -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors From d75b25faabdcf0a22fe37928917c4ab1761fa265 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 6 Oct 2022 09:44:38 +0200 Subject: [PATCH 005/524] Merge commit 'ac0e10aa68325235069a842f47499852b2dee79e' into clippyup --- CHANGELOG.md | 159 +++- Cargo.toml | 8 +- README.md | 46 +- clippy_dev/src/fmt.rs | 6 +- clippy_dev/src/main.rs | 2 +- clippy_dev/src/new_lint.rs | 167 ++-- clippy_dev/src/serve.rs | 4 +- clippy_dev/src/setup/git_hook.rs | 7 +- clippy_dev/src/setup/intellij.rs | 25 +- clippy_dev/src/setup/vscode.rs | 19 +- clippy_dev/src/update_lints.rs | 103 +- clippy_lints/Cargo.toml | 2 +- clippy_lints/src/approx_const.rs | 4 +- clippy_lints/src/asm_syntax.rs | 6 +- clippy_lints/src/assertions_on_constants.rs | 4 +- .../src/assertions_on_result_states.rs | 10 +- clippy_lints/src/attrs.rs | 7 +- clippy_lints/src/await_holding_invalid.rs | 42 +- clippy_lints/src/blocks_in_if_conditions.rs | 68 +- clippy_lints/src/bool_assert_comparison.rs | 4 +- clippy_lints/src/bool_to_int_with_if.rs | 21 +- clippy_lints/src/booleans.rs | 5 +- clippy_lints/src/box_default.rs | 61 ++ clippy_lints/src/cargo/common_metadata.rs | 2 +- clippy_lints/src/cargo/feature_name.rs | 4 +- clippy_lints/src/cargo/mod.rs | 4 +- .../src/cargo/multiple_crate_versions.rs | 2 +- clippy_lints/src/casts/borrow_as_ptr.rs | 2 +- clippy_lints/src/casts/cast_lossless.rs | 12 +- .../src/casts/cast_possible_truncation.rs | 16 +- clippy_lints/src/casts/cast_possible_wrap.rs | 5 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 4 +- clippy_lints/src/casts/cast_sign_loss.rs | 5 +- .../src/casts/cast_slice_different_sizes.rs | 4 +- clippy_lints/src/casts/char_lit_as_u8.rs | 2 +- clippy_lints/src/casts/fn_to_numeric_cast.rs | 4 +- .../src/casts/fn_to_numeric_cast_any.rs | 4 +- .../fn_to_numeric_cast_with_truncation.rs | 7 +- clippy_lints/src/casts/ptr_as_ptr.rs | 4 +- clippy_lints/src/casts/unnecessary_cast.rs | 81 +- clippy_lints/src/checked_conversions.rs | 16 +- clippy_lints/src/cognitive_complexity.rs | 64 +- clippy_lints/src/default.rs | 17 +- .../src/default_instead_of_iter_empty.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 4 +- .../src/default_union_representation.rs | 2 +- clippy_lints/src/dereference.rs | 53 +- clippy_lints/src/derive.rs | 2 +- clippy_lints/src/disallowed_macros.rs | 151 +++ clippy_lints/src/disallowed_methods.rs | 17 +- clippy_lints/src/disallowed_script_idents.rs | 3 +- clippy_lints/src/disallowed_types.rs | 24 +- clippy_lints/src/doc.rs | 92 +- clippy_lints/src/doc_link_with_quotes.rs | 60 -- clippy_lints/src/drop_forget_ref.rs | 30 +- clippy_lints/src/entry.rs | 28 +- clippy_lints/src/enum_variants.rs | 7 +- clippy_lints/src/equatable_if_let.rs | 7 +- clippy_lints/src/escape.rs | 10 +- clippy_lints/src/eta_reduction.rs | 19 +- clippy_lints/src/exhaustive_items.rs | 2 +- clippy_lints/src/explicit_write.rs | 8 +- clippy_lints/src/float_literal.rs | 6 +- clippy_lints/src/floating_point_arithmetic.rs | 55 +- clippy_lints/src/format.rs | 2 +- clippy_lints/src/format_args.rs | 154 ++- clippy_lints/src/format_impl.rs | 6 +- clippy_lints/src/formatting.rs | 25 +- clippy_lints/src/from_str_radix_10.rs | 6 +- clippy_lints/src/functions/must_use.rs | 82 +- .../src/functions/not_unsafe_ptr_arg_deref.rs | 91 +- .../src/functions/too_many_arguments.rs | 5 +- clippy_lints/src/functions/too_many_lines.rs | 5 +- clippy_lints/src/if_then_some_else_none.rs | 19 +- clippy_lints/src/implicit_hasher.rs | 9 +- clippy_lints/src/implicit_return.rs | 16 +- clippy_lints/src/implicit_saturating_add.rs | 114 +++ clippy_lints/src/implicit_saturating_sub.rs | 20 +- .../src/inconsistent_struct_constructor.rs | 6 +- clippy_lints/src/index_refutable_slice.rs | 4 +- clippy_lints/src/infinite_iter.rs | 15 +- clippy_lints/src/inherent_to_string.rs | 19 +- clippy_lints/src/inline_fn_without_body.rs | 2 +- clippy_lints/src/int_plus_one.rs | 4 +- .../src/iter_not_returning_iterator.rs | 5 +- clippy_lints/src/large_const_arrays.rs | 2 +- clippy_lints/src/len_zero.rs | 38 +- clippy_lints/src/let_if_seq.rs | 3 +- clippy_lints/src/lib.register_all.rs | 5 +- clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_internal.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 11 +- clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 3 +- clippy_lints/src/lib.register_perf.rs | 1 + clippy_lints/src/lib.register_style.rs | 3 +- clippy_lints/src/lib.rs | 56 +- clippy_lints/src/lifetimes.rs | 6 +- clippy_lints/src/literal_representation.rs | 2 +- .../src/loops/explicit_counter_loop.rs | 14 +- clippy_lints/src/loops/explicit_iter_loop.rs | 2 +- clippy_lints/src/loops/for_kv_map.rs | 4 +- clippy_lints/src/loops/manual_find.rs | 9 +- clippy_lints/src/loops/manual_flatten.rs | 14 +- clippy_lints/src/loops/manual_memcpy.rs | 13 +- clippy_lints/src/loops/mod.rs | 2 +- clippy_lints/src/loops/mut_range_bound.rs | 10 +- clippy_lints/src/loops/needless_collect.rs | 6 +- clippy_lints/src/loops/needless_range_loop.rs | 10 +- clippy_lints/src/loops/never_loop.rs | 37 +- clippy_lints/src/loops/same_item_push.rs | 5 +- clippy_lints/src/loops/utils.rs | 5 +- .../src/loops/while_let_on_iterator.rs | 12 +- clippy_lints/src/macro_use.rs | 6 +- clippy_lints/src/manual_assert.rs | 29 +- clippy_lints/src/manual_async_fn.rs | 6 +- clippy_lints/src/manual_clamp.rs | 713 ++++++++++++++ clippy_lints/src/manual_non_exhaustive.rs | 4 +- clippy_lints/src/manual_rem_euclid.rs | 2 +- clippy_lints/src/manual_retain.rs | 18 +- clippy_lints/src/manual_strip.rs | 9 +- clippy_lints/src/map_unit_fn.rs | 9 +- clippy_lints/src/match_result_ok.rs | 6 +- clippy_lints/src/matches/collapsible_match.rs | 6 +- clippy_lints/src/matches/manual_map.rs | 44 +- clippy_lints/src/matches/manual_unwrap_or.rs | 22 +- clippy_lints/src/matches/match_as_ref.rs | 18 +- .../src/matches/match_like_matches.rs | 5 +- clippy_lints/src/matches/match_same_arms.rs | 2 +- .../src/matches/match_single_binding.rs | 28 +- .../src/matches/match_str_case_mismatch.rs | 4 +- .../src/matches/match_wild_err_arm.rs | 2 +- clippy_lints/src/matches/needless_match.rs | 9 +- .../src/matches/redundant_pattern_match.rs | 65 +- .../matches/significant_drop_in_scrutinee.rs | 6 +- clippy_lints/src/matches/single_match.rs | 6 +- clippy_lints/src/matches/try_err.rs | 9 +- clippy_lints/src/mem_replace.rs | 72 +- .../src/methods/bind_instead_of_map.rs | 2 +- clippy_lints/src/methods/bytes_nth.rs | 2 +- clippy_lints/src/methods/chars_cmp.rs | 5 +- .../src/methods/chars_cmp_with_unwrap.rs | 5 +- clippy_lints/src/methods/clone_on_copy.rs | 13 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 11 +- clippy_lints/src/methods/filetype_is_file.rs | 11 +- clippy_lints/src/methods/filter_map_next.rs | 2 +- clippy_lints/src/methods/filter_next.rs | 2 +- .../methods/from_iter_instead_of_collect.rs | 6 +- clippy_lints/src/methods/get_first.rs | 4 +- clippy_lints/src/methods/get_last_with_len.rs | 6 +- clippy_lints/src/methods/get_unwrap.rs | 11 +- clippy_lints/src/methods/implicit_clone.rs | 6 +- .../src/methods/inefficient_to_string.rs | 9 +- clippy_lints/src/methods/into_iter_on_ref.rs | 3 +- .../src/methods/is_digit_ascii_radix.rs | 7 +- .../src/methods/iter_cloned_collect.rs | 4 +- clippy_lints/src/methods/iter_count.rs | 2 +- clippy_lints/src/methods/iter_kv_map.rs | 8 +- clippy_lints/src/methods/iter_next_slice.rs | 2 +- clippy_lints/src/methods/iter_nth.rs | 4 +- .../iter_on_single_or_empty_collections.rs | 27 +- clippy_lints/src/methods/iter_with_drain.rs | 2 +- clippy_lints/src/methods/manual_ok_or.rs | 38 +- .../methods/manual_saturating_arithmetic.rs | 5 +- clippy_lints/src/methods/manual_str_repeat.rs | 10 +- clippy_lints/src/methods/map_clone.rs | 5 +- clippy_lints/src/methods/map_flatten.rs | 9 +- clippy_lints/src/methods/map_identity.rs | 2 +- clippy_lints/src/methods/map_unwrap_or.rs | 2 +- clippy_lints/src/methods/mod.rs | 87 +- .../src/methods/option_as_ref_deref.rs | 9 +- .../src/methods/option_map_or_none.rs | 22 +- .../src/methods/option_map_unwrap_or.rs | 9 +- clippy_lints/src/methods/or_fun_call.rs | 10 +- clippy_lints/src/methods/or_then_unwrap.rs | 5 +- clippy_lints/src/methods/search_is_some.rs | 14 +- .../src/methods/single_char_insert_string.rs | 2 +- .../src/methods/single_char_push_string.rs | 2 +- .../src/methods/stable_sort_primitive.rs | 4 +- clippy_lints/src/methods/str_splitn.rs | 20 +- .../src/methods/string_extend_chars.rs | 3 +- clippy_lints/src/methods/suspicious_splitn.rs | 4 +- .../src/methods/suspicious_to_owned.rs | 4 +- .../src/methods/unnecessary_filter_map.rs | 73 +- clippy_lints/src/methods/unnecessary_fold.rs | 7 +- .../src/methods/unnecessary_iter_cloned.rs | 2 +- .../src/methods/unnecessary_lazy_eval.rs | 10 +- .../src/methods/unnecessary_to_owned.rs | 23 +- clippy_lints/src/methods/useless_asref.rs | 7 +- .../src/methods/wrong_self_convention.rs | 15 +- clippy_lints/src/minmax.rs | 4 +- clippy_lints/src/misc.rs | 24 +- clippy_lints/src/misc_early/literal_suffix.rs | 8 +- clippy_lints/src/misc_early/mod.rs | 5 +- .../src/misc_early/unneeded_field_pattern.rs | 4 +- .../src/mismatching_type_param_order.rs | 7 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/missing_doc.rs | 2 +- .../src/missing_enforced_import_rename.rs | 7 +- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/module_style.rs | 8 +- clippy_lints/src/mut_reference.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 5 +- clippy_lints/src/mutex_atomic.rs | 5 +- clippy_lints/src/needless_borrowed_ref.rs | 121 ++- clippy_lints/src/needless_continue.rs | 14 +- clippy_lints/src/needless_late_init.rs | 44 +- clippy_lints/src/needless_pass_by_value.rs | 17 +- clippy_lints/src/needless_question_mark.rs | 16 +- clippy_lints/src/neg_multiply.rs | 4 +- clippy_lints/src/new_without_default.rs | 7 +- clippy_lints/src/non_copy_const.rs | 5 +- clippy_lints/src/non_expressive_names.rs | 5 +- .../src/non_octal_unix_permissions.rs | 5 +- clippy_lints/src/nonstandard_macro_braces.rs | 79 +- clippy_lints/src/octal_escapes.rs | 2 +- .../operators/absurd_extreme_comparisons.rs | 5 +- .../src/operators/arithmetic_side_effects.rs | 120 ++- .../src/operators/assign_op_pattern.rs | 43 +- clippy_lints/src/operators/bit_mask.rs | 40 +- clippy_lints/src/operators/cmp_owned.rs | 10 +- clippy_lints/src/operators/duration_subsec.rs | 7 +- clippy_lints/src/operators/eq_op.rs | 2 +- .../src/operators/misrefactored_assign_op.rs | 12 +- clippy_lints/src/operators/mod.rs | 2 +- .../src/operators/needless_bitwise_bool.rs | 2 +- .../src/operators/numeric_arithmetic.rs | 9 +- clippy_lints/src/operators/ptr_eq.rs | 2 +- clippy_lints/src/operators/self_assignment.rs | 2 +- .../src/operators/verbose_bit_mask.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 30 +- clippy_lints/src/panic_in_result_fn.rs | 19 +- clippy_lints/src/partialeq_to_none.rs | 5 +- clippy_lints/src/pass_by_ref_or_value.rs | 4 +- clippy_lints/src/ptr.rs | 4 +- clippy_lints/src/ptr_offset_with_cast.rs | 4 +- clippy_lints/src/question_mark.rs | 39 +- clippy_lints/src/ranges.rs | 16 +- clippy_lints/src/read_zero_byte_vec.rs | 43 +- clippy_lints/src/redundant_pub_crate.rs | 2 +- clippy_lints/src/redundant_slicing.rs | 8 +- .../src/redundant_static_lifetimes.rs | 2 +- clippy_lints/src/regex.rs | 4 +- clippy_lints/src/returns.rs | 212 ++--- clippy_lints/src/same_name_method.rs | 4 +- .../src/semicolon_if_nothing_returned.rs | 2 +- .../src/slow_vector_initialization.rs | 16 +- clippy_lints/src/std_instead_of_core.rs | 22 + clippy_lints/src/strings.rs | 6 +- clippy_lints/src/strlen_on_c_strings.rs | 2 +- .../src/suspicious_operation_groupings.rs | 9 +- clippy_lints/src/suspicious_trait_impl.rs | 33 +- clippy_lints/src/swap.rs | 19 +- clippy_lints/src/swap_ptr_to_ref.rs | 2 +- clippy_lints/src/to_digit_is_some.rs | 4 +- clippy_lints/src/trait_bounds.rs | 3 +- .../src/transmute/crosspointer_transmute.rs | 10 +- .../src/transmute/transmute_float_to_int.rs | 4 +- .../src/transmute/transmute_int_to_bool.rs | 2 +- .../src/transmute/transmute_int_to_char.rs | 4 +- .../src/transmute/transmute_int_to_float.rs | 4 +- .../src/transmute/transmute_num_to_bytes.rs | 4 +- .../src/transmute/transmute_ptr_to_ref.rs | 18 +- .../src/transmute/transmute_ref_to_ref.rs | 2 +- .../src/transmute/transmute_undefined_repr.rs | 23 +- .../transmutes_expressible_as_ptr_casts.rs | 5 +- .../src/transmute/transmuting_null.rs | 41 +- .../transmute/unsound_collection_transmute.rs | 5 +- .../src/transmute/useless_transmute.rs | 2 +- clippy_lints/src/transmute/utils.rs | 5 +- clippy_lints/src/transmute/wrong_transmute.rs | 2 +- clippy_lints/src/types/borrowed_box.rs | 6 +- clippy_lints/src/types/box_collection.rs | 2 +- clippy_lints/src/types/mod.rs | 8 +- clippy_lints/src/types/rc_buffer.rs | 4 +- .../src/types/redundant_allocation.rs | 31 +- clippy_lints/src/types/vec_box.rs | 2 +- clippy_lints/src/uninit_vec.rs | 9 +- clippy_lints/src/unit_return_expecting_ord.rs | 6 +- clippy_lints/src/unit_types/unit_arg.rs | 7 +- clippy_lints/src/unit_types/unit_cmp.rs | 7 +- .../src/unnecessary_owned_empty_strings.rs | 4 +- clippy_lints/src/unnecessary_self_imports.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 13 +- clippy_lints/src/unsafe_removed_from_name.rs | 5 +- clippy_lints/src/unused_io_amount.rs | 7 +- clippy_lints/src/unused_rounding.rs | 4 +- clippy_lints/src/unwrap.rs | 7 +- clippy_lints/src/unwrap_in_result.rs | 72 +- clippy_lints/src/upper_case_acronyms.rs | 3 +- clippy_lints/src/use_self.rs | 2 +- clippy_lints/src/useless_conversion.rs | 14 +- clippy_lints/src/utils/author.rs | 2 +- clippy_lints/src/utils/conf.rs | 41 +- clippy_lints/src/utils/internal_lints.rs | 307 ++++-- .../internal_lints/metadata_collector.rs | 97 +- clippy_lints/src/wildcard_imports.rs | 2 +- clippy_lints/src/write.rs | 17 +- clippy_lints/src/zero_div_zero.rs | 3 +- clippy_lints/src/zero_sized_map_values.rs | 7 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/src/attrs.rs | 4 +- clippy_utils/src/diagnostics.rs | 5 +- clippy_utils/src/eager_or_lazy.rs | 12 +- clippy_utils/src/hir_utils.rs | 4 +- clippy_utils/src/lib.rs | 228 +++-- clippy_utils/src/macros.rs | 261 +++-- clippy_utils/src/msrvs.rs | 3 +- clippy_utils/src/paths.rs | 3 - clippy_utils/src/ptr.rs | 37 +- clippy_utils/src/qualify_min_const_fn.rs | 21 +- clippy_utils/src/source.rs | 18 +- clippy_utils/src/sugg.rs | 62 +- clippy_utils/src/ty.rs | 2 +- clippy_utils/src/usage.rs | 59 +- clippy_utils/src/visitors.rs | 164 ++-- lintcheck/Cargo.toml | 2 + lintcheck/README.md | 20 +- lintcheck/lintcheck_crates.toml | 8 + lintcheck/src/config.rs | 10 +- lintcheck/src/driver.rs | 67 ++ lintcheck/src/main.rs | 164 ++-- lintcheck/src/recursive.rs | 123 +++ rust-toolchain | 2 +- rustc_tools_util/Cargo.toml | 2 +- rustc_tools_util/README.md | 8 +- rustc_tools_util/src/lib.rs | 12 +- src/docs.rs | 5 + src/docs/arithmetic_side_effects.txt | 2 +- src/docs/box_default.txt | 23 + src/docs/disallowed_macros.txt | 36 + src/docs/implicit_saturating_add.txt | 20 + src/docs/manual_clamp.txt | 46 + src/docs/needless_borrowed_reference.txt | 18 +- src/docs/similar_names.txt | 4 + src/docs/uninlined_format_args.txt | 36 + src/driver.rs | 6 +- src/main.rs | 6 +- tests/compile-test.rs | 23 +- tests/integration.rs | 23 +- tests/lint_message_convention.rs | 2 +- tests/missing-test-files.rs | 2 +- .../duplicate_mod/fail/src/main.stderr | 2 +- .../feature_name/fail/src/main.stderr | 4 +- .../module_style/fail_mod/src/main.stderr | 2 +- .../fail_mod_remap/src/main.stderr | 2 +- .../module_style/fail_no_mod/src/main.stderr | 2 +- tests/ui-internal/auxiliary/paths.rs | 2 + .../check_clippy_version_attribute.stderr | 4 +- tests/ui-internal/if_chain_style.stderr | 2 +- tests/ui-internal/match_type_on_diag_item.rs | 39 - .../match_type_on_diag_item.stderr | 27 - tests/ui-internal/unnecessary_def_path.fixed | 62 ++ tests/ui-internal/unnecessary_def_path.rs | 62 ++ tests/ui-internal/unnecessary_def_path.stderr | 101 ++ .../conf_deprecated_key.rs | 2 + .../conf_deprecated_key.stderr | 2 +- .../disallowed_macros/auxiliary/macros.rs | 32 + tests/ui-toml/disallowed_macros/clippy.toml | 11 + .../disallowed_macros/disallowed_macros.rs | 39 + .../disallowed_macros.stderr | 84 ++ .../conf_nonstandard_macro_braces.fixed | 62 ++ .../conf_nonstandard_macro_braces.rs | 1 + .../conf_nonstandard_macro_braces.stderr | 81 +- .../toml_disallowed_methods/clippy.toml | 1 + .../conf_disallowed_methods.rs | 6 + .../conf_disallowed_methods.stderr | 24 +- .../toml_unknown_key/conf_unknown_key.stderr | 1 + tests/ui/arithmetic_side_effects.rs | 95 +- tests/ui/arithmetic_side_effects.stderr | 296 +++++- tests/ui/assign_ops2.rs | 2 + tests/ui/assign_ops2.stderr | 20 +- tests/ui/auxiliary/proc_macro_attr.rs | 2 +- tests/ui/bind_instead_of_map.fixed | 1 + tests/ui/bind_instead_of_map.rs | 1 + tests/ui/bind_instead_of_map.stderr | 6 +- tests/ui/borrow_box.rs | 5 +- tests/ui/borrow_box.stderr | 20 +- tests/ui/box_collection.rs | 4 +- tests/ui/box_default.rs | 31 + tests/ui/box_default.stderr | 59 ++ .../branches_sharing_code/shared_at_bottom.rs | 3 +- .../shared_at_bottom.stderr | 20 +- .../ui/branches_sharing_code/shared_at_top.rs | 5 +- .../shared_at_top.stderr | 28 +- .../shared_at_top_and_bottom.rs | 3 +- .../shared_at_top_and_bottom.stderr | 26 +- .../branches_sharing_code/valid_if_blocks.rs | 5 +- .../valid_if_blocks.stderr | 26 +- tests/ui/cast_abs_to_unsigned.fixed | 1 + tests/ui/cast_abs_to_unsigned.rs | 1 + tests/ui/cast_abs_to_unsigned.stderr | 34 +- tests/ui/collapsible_match.rs | 3 +- tests/ui/collapsible_match.stderr | 40 +- tests/ui/crashes/ice-4775.rs | 2 + tests/ui/crashes/ice-9445.rs | 3 + tests/ui/crashes/ice-9459.rs | 5 + tests/ui/crashes/regressions.rs | 2 +- tests/ui/default_trait_access.fixed | 4 +- tests/ui/default_trait_access.rs | 4 +- tests/ui/default_trait_access.stderr | 2 +- tests/ui/doc_link_with_quotes.rs | 7 +- tests/ui/doc_link_with_quotes.stderr | 6 +- tests/ui/drop_forget_copy.rs | 20 + tests/ui/drop_forget_copy.stderr | 38 +- tests/ui/eta.fixed | 25 +- tests/ui/eta.rs | 25 +- tests/ui/eta.stderr | 20 +- tests/ui/expect_fun_call.fixed | 9 +- tests/ui/expect_fun_call.rs | 9 +- tests/ui/expect_fun_call.stderr | 40 +- tests/ui/explicit_counter_loop.rs | 1 + tests/ui/explicit_counter_loop.stderr | 18 +- tests/ui/explicit_deref_methods.fixed | 10 +- tests/ui/explicit_deref_methods.rs | 10 +- tests/ui/explicit_write.fixed | 3 +- tests/ui/explicit_write.rs | 3 +- tests/ui/explicit_write.stderr | 26 +- tests/ui/fallible_impl_from.rs | 1 + tests/ui/fallible_impl_from.stderr | 16 +- tests/ui/floating_point_exp.fixed | 1 + tests/ui/floating_point_exp.rs | 1 + tests/ui/floating_point_exp.stderr | 10 +- tests/ui/floating_point_log.fixed | 2 +- tests/ui/floating_point_log.rs | 2 +- tests/ui/floating_point_logbase.fixed | 1 + tests/ui/floating_point_logbase.rs | 1 + tests/ui/floating_point_logbase.stderr | 10 +- tests/ui/floating_point_mul_add.fixed | 2 + tests/ui/floating_point_mul_add.rs | 2 + tests/ui/floating_point_mul_add.stderr | 30 +- tests/ui/floating_point_powf.fixed | 1 + tests/ui/floating_point_powf.rs | 1 + tests/ui/floating_point_powf.stderr | 62 +- tests/ui/floating_point_powi.fixed | 3 + tests/ui/floating_point_powi.rs | 3 + tests/ui/floating_point_powi.stderr | 24 +- tests/ui/for_loop_fixable.fixed | 2 +- tests/ui/for_loop_fixable.rs | 2 +- tests/ui/for_loops_over_fallibles.rs | 1 + tests/ui/for_loops_over_fallibles.stderr | 22 +- tests/ui/format.fixed | 6 +- tests/ui/format.rs | 6 +- tests/ui/format_args.fixed | 12 +- tests/ui/format_args.rs | 12 +- tests/ui/format_args.stderr | 46 +- tests/ui/format_args_unfixable.rs | 6 +- tests/ui/format_args_unfixable.stderr | 36 +- tests/ui/functions.rs | 4 +- tests/ui/identity_op.fixed | 4 +- tests/ui/identity_op.rs | 4 +- tests/ui/implicit_saturating_add.fixed | 106 +++ tests/ui/implicit_saturating_add.rs | 154 +++ tests/ui/implicit_saturating_add.stderr | 197 ++++ .../if_let_slice_binding.rs | 1 + .../if_let_slice_binding.stderr | 20 +- tests/ui/infinite_iter.rs | 2 + tests/ui/infinite_iter.stderr | 32 +- tests/ui/issue_2356.fixed | 1 + tests/ui/issue_2356.rs | 1 + tests/ui/issue_2356.stderr | 2 +- tests/ui/issue_4266.rs | 1 + tests/ui/issue_4266.stderr | 6 +- tests/ui/item_after_statement.rs | 1 + tests/ui/item_after_statement.stderr | 6 +- tests/ui/manual_assert.edition2018.fixed | 14 +- tests/ui/manual_assert.edition2018.stderr | 90 +- tests/ui/manual_assert.edition2021.fixed | 14 +- tests/ui/manual_assert.edition2021.stderr | 90 +- tests/ui/manual_assert.fixed | 45 - tests/ui/manual_assert.rs | 15 +- tests/ui/manual_bits.fixed | 3 +- tests/ui/manual_bits.rs | 3 +- tests/ui/manual_bits.stderr | 58 +- tests/ui/manual_clamp.rs | 304 ++++++ tests/ui/manual_clamp.stderr | 375 ++++++++ tests/ui/manual_find_fixable.fixed | 4 +- tests/ui/manual_find_fixable.rs | 4 +- tests/ui/manual_flatten.rs | 2 +- tests/ui/map_unwrap_or.rs | 2 +- tests/ui/match_ref_pats.fixed | 3 +- tests/ui/match_ref_pats.rs | 3 +- tests/ui/match_ref_pats.stderr | 10 +- tests/ui/match_result_ok.fixed | 3 +- tests/ui/match_result_ok.rs | 3 +- tests/ui/match_result_ok.stderr | 6 +- tests/ui/match_same_arms2.rs | 6 +- tests/ui/match_same_arms2.stderr | 46 +- tests/ui/match_single_binding.fixed | 4 +- tests/ui/match_single_binding.rs | 4 +- tests/ui/match_single_binding2.fixed | 2 +- tests/ui/match_single_binding2.rs | 2 +- tests/ui/min_max.rs | 1 + tests/ui/min_max.stderr | 26 +- tests/ui/min_rust_version_attr.rs | 12 + tests/ui/min_rust_version_attr.stderr | 8 +- tests/ui/mut_mut.rs | 4 +- tests/ui/needless_borrow.fixed | 33 +- tests/ui/needless_borrow.rs | 33 +- tests/ui/needless_borrowed_ref.fixed | 41 +- tests/ui/needless_borrowed_ref.rs | 41 +- tests/ui/needless_borrowed_ref.stderr | 121 ++- tests/ui/needless_collect_indirect.rs | 2 + tests/ui/needless_collect_indirect.stderr | 32 +- tests/ui/needless_continue.rs | 1 + tests/ui/needless_continue.stderr | 16 +- tests/ui/needless_for_each_fixable.fixed | 7 +- tests/ui/needless_for_each_fixable.rs | 7 +- tests/ui/needless_for_each_fixable.stderr | 16 +- tests/ui/needless_for_each_unfixable.rs | 2 +- tests/ui/needless_late_init.fixed | 5 +- tests/ui/needless_late_init.rs | 5 +- tests/ui/needless_late_init.stderr | 32 +- tests/ui/needless_pass_by_value.rs | 9 +- tests/ui/needless_pass_by_value.stderr | 52 +- tests/ui/needless_range_loop.rs | 1 + tests/ui/needless_range_loop.stderr | 28 +- tests/ui/needless_return.fixed | 37 + tests/ui/needless_return.rs | 37 + tests/ui/needless_return.stderr | 205 +++- tests/ui/never_loop.rs | 26 + tests/ui/never_loop.stderr | 15 +- tests/ui/no_effect.rs | 6 +- tests/ui/no_effect.stderr | 60 +- tests/ui/option_map_unit_fn_fixable.fixed | 3 +- tests/ui/option_map_unit_fn_fixable.rs | 3 +- tests/ui/option_map_unit_fn_fixable.stderr | 38 +- tests/ui/option_take_on_temporary.fixed | 15 - tests/ui/or_fun_call.fixed | 3 +- tests/ui/or_fun_call.rs | 3 +- tests/ui/or_fun_call.stderr | 52 +- tests/ui/panic_in_result_fn_assertions.rs | 2 +- .../ui/panic_in_result_fn_debug_assertions.rs | 2 +- tests/ui/patterns.fixed | 3 +- tests/ui/patterns.rs | 3 +- tests/ui/patterns.stderr | 6 +- tests/ui/print_literal.rs | 1 + tests/ui/print_literal.stderr | 24 +- tests/ui/ptr_offset_with_cast.fixed | 1 + tests/ui/ptr_offset_with_cast.rs | 1 + tests/ui/ptr_offset_with_cast.stderr | 4 +- tests/ui/question_mark.fixed | 9 + tests/ui/question_mark.rs | 9 + tests/ui/recursive_format_impl.rs | 5 +- tests/ui/recursive_format_impl.stderr | 20 +- tests/ui/redundant_clone.fixed | 4 +- tests/ui/redundant_clone.rs | 4 +- .../redundant_pattern_matching_ipaddr.fixed | 11 +- tests/ui/redundant_pattern_matching_ipaddr.rs | 11 +- .../redundant_pattern_matching_ipaddr.stderr | 36 +- .../redundant_pattern_matching_result.fixed | 11 +- tests/ui/redundant_pattern_matching_result.rs | 11 +- .../redundant_pattern_matching_result.stderr | 44 +- tests/ui/result_map_unit_fn_fixable.fixed | 2 +- tests/ui/result_map_unit_fn_fixable.rs | 2 +- tests/ui/reversed_empty_ranges_fixable.fixed | 1 + tests/ui/reversed_empty_ranges_fixable.rs | 1 + tests/ui/reversed_empty_ranges_fixable.stderr | 8 +- .../reversed_empty_ranges_loops_fixable.fixed | 1 + .../ui/reversed_empty_ranges_loops_fixable.rs | 1 + ...reversed_empty_ranges_loops_fixable.stderr | 12 +- .../reversed_empty_ranges_loops_unfixable.rs | 1 + ...versed_empty_ranges_loops_unfixable.stderr | 4 +- tests/ui/same_functions_in_if_condition.rs | 12 +- .../ui/same_functions_in_if_condition.stderr | 24 +- tests/ui/semicolon_if_nothing_returned.rs | 2 +- .../ui/should_impl_trait/method_list_1.stderr | 12 +- tests/ui/significant_drop_in_scrutinee.rs | 7 +- tests/ui/significant_drop_in_scrutinee.stderr | 52 +- tests/ui/single_match.rs | 1 + tests/ui/single_match.stderr | 32 +- tests/ui/single_match_else.rs | 4 +- tests/ui/single_match_else.stderr | 10 +- tests/ui/std_instead_of_core.rs | 6 + tests/ui/std_instead_of_core.stderr | 16 +- tests/ui/toplevel_ref_arg.fixed | 2 +- tests/ui/toplevel_ref_arg.rs | 2 +- tests/ui/trivially_copy_pass_by_ref.rs | 7 +- tests/ui/trivially_copy_pass_by_ref.stderr | 38 +- tests/ui/uninit_vec.rs | 6 + tests/ui/uninlined_format_args.fixed | 164 ++++ tests/ui/uninlined_format_args.rs | 169 ++++ tests/ui/uninlined_format_args.stderr | 894 ++++++++++++++++++ tests/ui/unit_arg.rs | 15 +- tests/ui/unit_arg.stderr | 20 +- tests/ui/unit_arg_empty_blocks.fixed | 3 +- tests/ui/unit_arg_empty_blocks.rs | 3 +- tests/ui/unit_arg_empty_blocks.stderr | 8 +- tests/ui/unnecessary_cast.fixed | 14 + tests/ui/unnecessary_cast.rs | 14 + tests/ui/unnecessary_cast.stderr | 20 +- tests/ui/unnecessary_clone.rs | 4 +- tests/ui/unnecessary_join.fixed | 2 +- tests/ui/unnecessary_join.rs | 2 +- tests/ui/unnecessary_lazy_eval.fixed | 21 + tests/ui/unnecessary_lazy_eval.rs | 21 + tests/ui/unnecessary_lazy_eval.stderr | 68 +- tests/ui/upper_case_acronyms.rs | 9 + tests/ui/upper_case_acronyms.stderr | 14 +- tests/ui/used_underscore_binding.rs | 3 +- tests/ui/used_underscore_binding.stderr | 12 +- tests/ui/useless_asref.fixed | 3 +- tests/ui/useless_asref.rs | 3 +- tests/ui/useless_asref.stderr | 24 +- tests/ui/vec.fixed | 2 +- tests/ui/vec.rs | 2 +- tests/ui/while_let_loop.rs | 1 + tests/ui/while_let_loop.stderr | 10 +- tests/ui/while_let_on_iterator.fixed | 10 +- tests/ui/while_let_on_iterator.rs | 10 +- tests/ui/while_let_on_iterator.stderr | 52 +- tests/ui/wildcard_enum_match_arm.fixed | 10 +- tests/ui/wildcard_enum_match_arm.rs | 10 +- tests/ui/wildcard_enum_match_arm.stderr | 14 +- tests/ui/write_literal.rs | 2 +- tests/versioncheck.rs | 6 +- 617 files changed, 10205 insertions(+), 4346 deletions(-) create mode 100644 clippy_lints/src/box_default.rs create mode 100644 clippy_lints/src/disallowed_macros.rs delete mode 100644 clippy_lints/src/doc_link_with_quotes.rs create mode 100644 clippy_lints/src/implicit_saturating_add.rs create mode 100644 clippy_lints/src/manual_clamp.rs create mode 100644 lintcheck/src/driver.rs create mode 100644 lintcheck/src/recursive.rs create mode 100644 src/docs/box_default.txt create mode 100644 src/docs/disallowed_macros.txt create mode 100644 src/docs/implicit_saturating_add.txt create mode 100644 src/docs/manual_clamp.txt create mode 100644 src/docs/uninlined_format_args.txt create mode 100644 tests/ui-internal/auxiliary/paths.rs delete mode 100644 tests/ui-internal/match_type_on_diag_item.rs delete mode 100644 tests/ui-internal/match_type_on_diag_item.stderr create mode 100644 tests/ui-internal/unnecessary_def_path.fixed create mode 100644 tests/ui-internal/unnecessary_def_path.rs create mode 100644 tests/ui-internal/unnecessary_def_path.stderr create mode 100644 tests/ui-toml/disallowed_macros/auxiliary/macros.rs create mode 100644 tests/ui-toml/disallowed_macros/clippy.toml create mode 100644 tests/ui-toml/disallowed_macros/disallowed_macros.rs create mode 100644 tests/ui-toml/disallowed_macros/disallowed_macros.stderr create mode 100644 tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed create mode 100644 tests/ui/box_default.rs create mode 100644 tests/ui/box_default.stderr create mode 100644 tests/ui/crashes/ice-9445.rs create mode 100644 tests/ui/crashes/ice-9459.rs create mode 100644 tests/ui/implicit_saturating_add.fixed create mode 100644 tests/ui/implicit_saturating_add.rs create mode 100644 tests/ui/implicit_saturating_add.stderr delete mode 100644 tests/ui/manual_assert.fixed create mode 100644 tests/ui/manual_clamp.rs create mode 100644 tests/ui/manual_clamp.stderr delete mode 100644 tests/ui/option_take_on_temporary.fixed create mode 100644 tests/ui/uninlined_format_args.fixed create mode 100644 tests/ui/uninlined_format_args.rs create mode 100644 tests/ui/uninlined_format_args.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 044cbff4b78e..42615179f705 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,161 @@ document. ## Unreleased / In Rust Nightly -[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master) +[3c7e7dbc...master](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...master) + +## Rust 1.64 + +Current stable, released 2022-09-22 + +[d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc) + +### New Lints + +* [`arithmetic_side_effects`] + [#9130](https://github.com/rust-lang/rust-clippy/pull/9130) +* [`invalid_utf8_in_unchecked`] + [#9105](https://github.com/rust-lang/rust-clippy/pull/9105) +* [`assertions_on_result_states`] + [#9225](https://github.com/rust-lang/rust-clippy/pull/9225) +* [`manual_find`] + [#8649](https://github.com/rust-lang/rust-clippy/pull/8649) +* [`manual_retain`] + [#8972](https://github.com/rust-lang/rust-clippy/pull/8972) +* [`default_instead_of_iter_empty`] + [#8989](https://github.com/rust-lang/rust-clippy/pull/8989) +* [`manual_rem_euclid`] + [#9031](https://github.com/rust-lang/rust-clippy/pull/9031) +* [`obfuscated_if_else`] + [#9148](https://github.com/rust-lang/rust-clippy/pull/9148) +* [`std_instead_of_core`] + [#9103](https://github.com/rust-lang/rust-clippy/pull/9103) +* [`std_instead_of_alloc`] + [#9103](https://github.com/rust-lang/rust-clippy/pull/9103) +* [`alloc_instead_of_core`] + [#9103](https://github.com/rust-lang/rust-clippy/pull/9103) +* [`explicit_auto_deref`] + [#8355](https://github.com/rust-lang/rust-clippy/pull/8355) + + +### Moves and Deprecations + +* Moved [`format_push_string`] to `restriction` (now allow-by-default) + [#9161](https://github.com/rust-lang/rust-clippy/pull/9161) + +### Enhancements + +* [`significant_drop_in_scrutinee`]: Now gives more context in the lint message + [#8981](https://github.com/rust-lang/rust-clippy/pull/8981) +* [`single_match`], [`single_match_else`]: Now catches more `Option` cases + [#8985](https://github.com/rust-lang/rust-clippy/pull/8985) +* [`unused_async`]: Now works for async methods + [#9025](https://github.com/rust-lang/rust-clippy/pull/9025) +* [`manual_filter_map`], [`manual_find_map`]: Now lint more expressions + [#8958](https://github.com/rust-lang/rust-clippy/pull/8958) +* [`question_mark`]: Now works for simple `if let` expressions + [#8356](https://github.com/rust-lang/rust-clippy/pull/8356) +* [`undocumented_unsafe_blocks`]: Now finds comments before the start of closures + [#9117](https://github.com/rust-lang/rust-clippy/pull/9117) +* [`trait_duplication_in_bounds`]: Now catches duplicate bounds in where clauses + [#8703](https://github.com/rust-lang/rust-clippy/pull/8703) +* [`shadow_reuse`], [`shadow_same`], [`shadow_unrelated`]: Now lint in const blocks + [#9124](https://github.com/rust-lang/rust-clippy/pull/9124) +* [`slow_vector_initialization`]: Now detects cases with `vec.capacity()` + [#8953](https://github.com/rust-lang/rust-clippy/pull/8953) +* [`unused_self`]: Now respects the `avoid-breaking-exported-api` config option + [#9199](https://github.com/rust-lang/rust-clippy/pull/9199) +* [`box_collection`]: Now supports all std collections + [#9170](https://github.com/rust-lang/rust-clippy/pull/9170) + +### False Positive Fixes + +* [`significant_drop_in_scrutinee`]: Now ignores calls to `IntoIterator::into_iter` + [#9140](https://github.com/rust-lang/rust-clippy/pull/9140) +* [`while_let_loop`]: Now ignores cases when the significant drop order would change + [#8981](https://github.com/rust-lang/rust-clippy/pull/8981) +* [`branches_sharing_code`]: Now ignores cases where moved variables have a significant + drop or variable modifications can affect the conditions + [#9138](https://github.com/rust-lang/rust-clippy/pull/9138) +* [`let_underscore_lock`]: Now ignores bindings that aren't locked + [#8990](https://github.com/rust-lang/rust-clippy/pull/8990) +* [`trivially_copy_pass_by_ref`]: Now tracks lifetimes and ignores cases where unsafe + pointers are used + [#8639](https://github.com/rust-lang/rust-clippy/pull/8639) +* [`let_unit_value`]: No longer ignores `#[allow]` attributes on the value + [#9082](https://github.com/rust-lang/rust-clippy/pull/9082) +* [`declare_interior_mutable_const`]: Now ignores the `thread_local!` macro + [#9015](https://github.com/rust-lang/rust-clippy/pull/9015) +* [`if_same_then_else`]: Now ignores branches with `todo!` and `unimplemented!` + [#9006](https://github.com/rust-lang/rust-clippy/pull/9006) +* [`enum_variant_names`]: Now ignores names with `_` prefixes + [#9032](https://github.com/rust-lang/rust-clippy/pull/9032) +* [`let_unit_value`]: Now ignores cases, where the unit type is manually specified + [#9056](https://github.com/rust-lang/rust-clippy/pull/9056) +* [`match_same_arms`]: Now ignores branches with `todo!` + [#9207](https://github.com/rust-lang/rust-clippy/pull/9207) +* [`assign_op_pattern`]: Ignores cases that break borrowing rules + [#9214](https://github.com/rust-lang/rust-clippy/pull/9214) +* [`extra_unused_lifetimes`]: No longer triggers in derive macros + [#9037](https://github.com/rust-lang/rust-clippy/pull/9037) +* [`mismatching_type_param_order`]: Now ignores complicated generic parameters + [#9146](https://github.com/rust-lang/rust-clippy/pull/9146) +* [`equatable_if_let`]: No longer lints in macros + [#9074](https://github.com/rust-lang/rust-clippy/pull/9074) +* [`new_without_default`]: Now ignores generics and lifetime parameters on `fn new` + [#9115](https://github.com/rust-lang/rust-clippy/pull/9115) +* [`needless_borrow`]: Now ignores cases that result in the execution of different traits + [#9096](https://github.com/rust-lang/rust-clippy/pull/9096) +* [`declare_interior_mutable_const`]: No longer triggers in thread-local initializers + [#9246](https://github.com/rust-lang/rust-clippy/pull/9246) + +### Suggestion Fixes/Improvements + +* [`type_repetition_in_bounds`]: The suggestion now works with maybe bounds + [#9132](https://github.com/rust-lang/rust-clippy/pull/9132) +* [`transmute_ptr_to_ref`]: Now suggests `pointer::cast` when possible + [#8939](https://github.com/rust-lang/rust-clippy/pull/8939) +* [`useless_format`]: Now suggests the correct variable name + [#9237](https://github.com/rust-lang/rust-clippy/pull/9237) +* [`or_fun_call`]: The lint emission will now only span over the `unwrap_or` call + [#9144](https://github.com/rust-lang/rust-clippy/pull/9144) +* [`neg_multiply`]: Now suggests adding parentheses around suggestion if needed + [#9026](https://github.com/rust-lang/rust-clippy/pull/9026) +* [`unnecessary_lazy_evaluations`]: Now suggest for `bool::then_some` for lazy evaluation + [#9099](https://github.com/rust-lang/rust-clippy/pull/9099) +* [`manual_flatten`]: Improved message for long code snippets + [#9156](https://github.com/rust-lang/rust-clippy/pull/9156) +* [`explicit_counter_loop`]: The suggestion is now machine applicable + [#9149](https://github.com/rust-lang/rust-clippy/pull/9149) +* [`needless_borrow`]: Now keeps parentheses around fields, when needed + [#9210](https://github.com/rust-lang/rust-clippy/pull/9210) +* [`while_let_on_iterator`]: The suggestion now works in `FnOnce` closures + [#9134](https://github.com/rust-lang/rust-clippy/pull/9134) + +### ICE Fixes + +* Fix ICEs related to `#![feature(generic_const_exprs)]` usage + [#9241](https://github.com/rust-lang/rust-clippy/pull/9241) +* Fix ICEs related to reference lints + [#9093](https://github.com/rust-lang/rust-clippy/pull/9093) +* [`question_mark`]: Fix ICE on zero field tuple structs + [#9244](https://github.com/rust-lang/rust-clippy/pull/9244) + +### Documentation Improvements + +* [`needless_option_take`]: Now includes a "What it does" and "Why is this bad?" section. + [#9022](https://github.com/rust-lang/rust-clippy/pull/9022) + +### Others + +* Using `--cap-lints=allow` and only `--force-warn`ing some will now work with Clippy's driver + [#9036](https://github.com/rust-lang/rust-clippy/pull/9036) +* Clippy now tries to read the `rust-version` from `Cargo.toml` to identify the + minimum supported rust version + [#8774](https://github.com/rust-lang/rust-clippy/pull/8774) ## Rust 1.63 -Current stable, released 2022-08-11 +Released 2022-08-11 [7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0) @@ -3609,6 +3759,7 @@ Released 2018-09-13 [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection +[`box_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_default [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code @@ -3669,6 +3820,7 @@ Released 2018-09-13 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq +[`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods [`disallowed_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names @@ -3766,6 +3918,7 @@ Released 2018-09-13 [`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 [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return +[`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping @@ -3834,6 +3987,7 @@ Released 2018-09-13 [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits +[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map @@ -4124,6 +4278,7 @@ Released 2018-09-13 [`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec +[`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp [`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash diff --git a/Cargo.toml b/Cargo.toml index b7e136ce9b29..60200a88b858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.65" +version = "0.1.66" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -23,12 +23,12 @@ path = "src/driver.rs" [dependencies] clippy_lints = { path = "clippy_lints" } semver = "1.0" -rustc_tools_util = { path = "rustc_tools_util" } +rustc_tools_util = "0.2.1" tempfile = { version = "3.2", optional = true } termize = "0.1" [dev-dependencies] -compiletest_rs = { version = "0.8", features = ["tmp"] } +compiletest_rs = { version = "0.9", features = ["tmp"] } tester = "0.9" regex = "1.5" toml = "0.5" @@ -55,7 +55,7 @@ tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" [build-dependencies] -rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } +rustc_tools_util = "0.2.1" [features] deny-warnings = ["clippy_lints/deny-warnings"] diff --git a/README.md b/README.md index 1193771ff736..a8a6b86d2a15 100644 --- a/README.md +++ b/README.md @@ -139,25 +139,6 @@ line. (You can swap `clippy::all` with the specific lint category you are target ## Configuration -Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable = -value` mapping e.g. - -```toml -avoid-breaking-exported-api = false -disallowed-names = ["toto", "tata", "titi"] -cognitive-complexity-threshold = 30 -``` - -See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which -lints can be configured and the meaning of the variables. - -Note that configuration changes will not apply for code that has already been compiled and cached under `./target/`; -for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure that -any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch. - -To deactivate the “for further information visit *lint-link*” message you can -define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. - ### Allowing/denying lints You can add options to your code to `allow`/`warn`/`deny` Clippy lints: @@ -205,6 +186,33 @@ the lint(s) you are interested in: cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... ``` +### Configure the behavior of some lints + +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable = +value` mapping e.g. + +```toml +avoid-breaking-exported-api = false +disallowed-names = ["toto", "tata", "titi"] +cognitive-complexity-threshold = 30 +``` + +See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which +lints can be configured and the meaning of the variables. + +> **Note** +> +> `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints. + +> **Note** +> +> Configuration changes will not apply for code that has already been compiled and cached under `./target/`; +> for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure +> that any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch. + +To deactivate the “for further information visit *lint-link*” message you can +define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. + ### Specifying the minimum supported Rust version Projects that intend to support old versions of Rust can disable lints pertaining to newer features by diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 357cf6fc43aa..256231441817 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -82,16 +82,16 @@ pub fn run(check: bool, verbose: bool) { fn output_err(err: CliError) { match err { CliError::CommandFailed(command, stderr) => { - eprintln!("error: A command failed! `{}`\nstderr: {}", command, stderr); + eprintln!("error: A command failed! `{command}`\nstderr: {stderr}"); }, CliError::IoError(err) => { - eprintln!("error: {}", err); + eprintln!("error: {err}"); }, CliError::RustfmtNotInstalled => { eprintln!("error: rustfmt nightly is not installed."); }, CliError::WalkDirError(err) => { - eprintln!("error: {}", err); + eprintln!("error: {err}"); }, CliError::IntellijSetupActive => { eprintln!( diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index a417d3dd8a4e..d3e036692040 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -41,7 +41,7 @@ fn main() { matches.contains_id("msrv"), ) { Ok(_) => update_lints::update(update_lints::UpdateMode::Change), - Err(e) => eprintln!("Unable to create lint: {}", e), + Err(e) => eprintln!("Unable to create lint: {e}"), } }, Some(("setup", sub_command)) => match sub_command.subcommand() { diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 02cb13a1d8af..9e15f1504fa9 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,5 +1,5 @@ use crate::clippy_project_root; -use indoc::{indoc, writedoc}; +use indoc::{formatdoc, writedoc}; use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; @@ -23,7 +23,7 @@ impl Context for io::Result { match self { Ok(t) => Ok(t), Err(e) => { - let message = format!("{}: {}", text.as_ref(), e); + let message = format!("{}: {e}", text.as_ref()); Err(io::Error::new(ErrorKind::Other, message)) }, } @@ -72,7 +72,7 @@ fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let lint_contents = get_lint_file_contents(lint, enable_msrv); let lint_path = format!("clippy_lints/src/{}.rs", lint.name); write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?; - println!("Generated lint file: `{}`", lint_path); + println!("Generated lint file: `{lint_path}`"); Ok(()) } @@ -86,7 +86,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { path.push("src"); fs::create_dir(&path)?; - let header = format!("// compile-flags: --crate-name={}", lint_name); + let header = format!("// compile-flags: --crate-name={lint_name}"); write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?; Ok(()) @@ -106,7 +106,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> { let test_contents = get_test_file_contents(lint.name, None); write_file(lint.project_root.join(&test_path), test_contents)?; - println!("Generated test file: `{}`", test_path); + println!("Generated test file: `{test_path}`"); } Ok(()) @@ -186,38 +186,36 @@ pub(crate) fn get_stabilization_version() -> String { } fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { - let mut contents = format!( - indoc! {" - #![allow(unused)] - #![warn(clippy::{})] - - fn main() {{ - // test code goes here - }} - "}, - lint_name + let mut contents = formatdoc!( + r#" + #![allow(unused)] + #![warn(clippy::{lint_name})] + + fn main() {{ + // test code goes here + }} + "# ); if let Some(header) = header_commands { - contents = format!("{}\n{}", header, contents); + contents = format!("{header}\n{contents}"); } contents } fn get_manifest_contents(lint_name: &str, hint: &str) -> String { - format!( - indoc! {r#" - # {} - - [package] - name = "{}" - version = "0.1.0" - publish = false - - [workspace] - "#}, - hint, lint_name + formatdoc!( + r#" + # {hint} + + [package] + name = "{lint_name}" + version = "0.1.0" + publish = false + + [workspace] + "# ) } @@ -238,76 +236,61 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { let name_upper = lint_name.to_uppercase(); result.push_str(&if enable_msrv { - format!( - indoc! {" - use clippy_utils::msrvs; - {pass_import} - use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; - use rustc_semver::RustcVersion; - use rustc_session::{{declare_tool_lint, impl_lint_pass}}; + formatdoc!( + r#" + use clippy_utils::msrvs; + {pass_import} + use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; + use rustc_semver::RustcVersion; + use rustc_session::{{declare_tool_lint, impl_lint_pass}}; - "}, - pass_type = pass_type, - pass_import = pass_import, - context_import = context_import, + "# ) } else { - format!( - indoc! {" - {pass_import} - use rustc_lint::{{{context_import}, {pass_type}}}; - use rustc_session::{{declare_lint_pass, declare_tool_lint}}; - - "}, - pass_import = pass_import, - pass_type = pass_type, - context_import = context_import + formatdoc!( + r#" + {pass_import} + use rustc_lint::{{{context_import}, {pass_type}}}; + use rustc_session::{{declare_lint_pass, declare_tool_lint}}; + + "# ) }); let _ = write!(result, "{}", get_lint_declaration(&name_upper, category)); result.push_str(&if enable_msrv { - format!( - indoc! {" - pub struct {name_camel} {{ - msrv: Option, - }} + formatdoc!( + r#" + pub struct {name_camel} {{ + msrv: Option, + }} - impl {name_camel} {{ - #[must_use] - pub fn new(msrv: Option) -> Self {{ - Self {{ msrv }} - }} + impl {name_camel} {{ + #[must_use] + pub fn new(msrv: Option) -> Self {{ + Self {{ msrv }} }} + }} - impl_lint_pass!({name_camel} => [{name_upper}]); + impl_lint_pass!({name_camel} => [{name_upper}]); - impl {pass_type}{pass_lifetimes} for {name_camel} {{ - extract_msrv_attr!({context_import}); - }} + impl {pass_type}{pass_lifetimes} for {name_camel} {{ + extract_msrv_attr!({context_import}); + }} - // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. - // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. - // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` - "}, - pass_type = pass_type, - pass_lifetimes = pass_lifetimes, - name_upper = name_upper, - name_camel = name_camel, - context_import = context_import, + // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. + // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`. + // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` + "# ) } else { - format!( - indoc! {" - declare_lint_pass!({name_camel} => [{name_upper}]); + formatdoc!( + r#" + declare_lint_pass!({name_camel} => [{name_upper}]); - impl {pass_type}{pass_lifetimes} for {name_camel} {{}} - "}, - pass_type = pass_type, - pass_lifetimes = pass_lifetimes, - name_upper = name_upper, - name_camel = name_camel, + impl {pass_type}{pass_lifetimes} for {name_camel} {{}} + "# ) }); @@ -315,8 +298,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { } fn get_lint_declaration(name_upper: &str, category: &str) -> String { - format!( - indoc! {r#" + formatdoc!( + r#" declare_clippy_lint! {{ /// ### What it does /// @@ -330,15 +313,13 @@ fn get_lint_declaration(name_upper: &str, category: &str) -> String { /// ```rust /// // example code which does not raise clippy warning /// ``` - #[clippy::version = "{version}"] + #[clippy::version = "{}"] pub {name_upper}, {category}, "default lint description" }} - "#}, - version = get_stabilization_version(), - name_upper = name_upper, - category = category, + "#, + get_stabilization_version(), ) } @@ -352,7 +333,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R _ => {}, } - let ty_dir = lint.project_root.join(format!("clippy_lints/src/{}", ty)); + let ty_dir = lint.project_root.join(format!("clippy_lints/src/{ty}")); assert!( ty_dir.exists() && ty_dir.is_dir(), "Directory `{}` does not exist!", @@ -412,10 +393,10 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R } write_file(lint_file_path.as_path(), lint_file_contents)?; - println!("Generated lint file: `clippy_lints/src/{}/{}.rs`", ty, lint.name); + println!("Generated lint file: `clippy_lints/src/{ty}/{}.rs`", lint.name); println!( - "Be sure to add a call to `{}::check` in `clippy_lints/src/{}/mod.rs`!", - lint.name, ty + "Be sure to add a call to `{}::check` in `clippy_lints/src/{ty}/mod.rs`!", + lint.name ); Ok(()) @@ -542,7 +523,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> .chain(std::iter::once(&*lint_name_upper)) .filter(|s| !s.is_empty()) { - let _ = write!(new_arr_content, "\n {},", ident); + let _ = write!(new_arr_content, "\n {ident},"); } new_arr_content.push('\n'); diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index f15f24da9467..2e0794f12fa1 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -10,8 +10,8 @@ use std::time::{Duration, SystemTime}; /// Panics if the python commands could not be spawned pub fn run(port: u16, lint: Option<&String>) -> ! { let mut url = Some(match lint { - None => format!("http://localhost:{}", port), - Some(lint) => format!("http://localhost:{}/#{}", port, lint), + None => format!("http://localhost:{port}"), + Some(lint) => format!("http://localhost:{port}/#{lint}"), }); loop { diff --git a/clippy_dev/src/setup/git_hook.rs b/clippy_dev/src/setup/git_hook.rs index 3fbb77d59235..1de5b1940bae 100644 --- a/clippy_dev/src/setup/git_hook.rs +++ b/clippy_dev/src/setup/git_hook.rs @@ -30,10 +30,7 @@ pub fn install_hook(force_override: bool) { println!("info: the hook can be removed with `cargo dev remove git-hook`"); println!("git hook successfully installed"); }, - Err(err) => eprintln!( - "error: unable to copy `{}` to `{}` ({})", - HOOK_SOURCE_FILE, HOOK_TARGET_FILE, err - ), + Err(err) => eprintln!("error: unable to copy `{HOOK_SOURCE_FILE}` to `{HOOK_TARGET_FILE}` ({err})"), } } @@ -77,7 +74,7 @@ pub fn remove_hook() { fn delete_git_hook_file(path: &Path) -> bool { if let Err(err) = fs::remove_file(path) { - eprintln!("error: unable to delete existing pre-commit git hook ({})", err); + eprintln!("error: unable to delete existing pre-commit git hook ({err})"); false } else { true diff --git a/clippy_dev/src/setup/intellij.rs b/clippy_dev/src/setup/intellij.rs index bf741e6d1217..b64e79733eb2 100644 --- a/clippy_dev/src/setup/intellij.rs +++ b/clippy_dev/src/setup/intellij.rs @@ -60,7 +60,7 @@ fn check_and_get_rustc_dir(rustc_path: &str) -> Result { path = absolute_path; }, Err(err) => { - eprintln!("error: unable to get the absolute path of rustc ({})", err); + eprintln!("error: unable to get the absolute path of rustc ({err})"); return Err(()); }, }; @@ -103,14 +103,14 @@ fn inject_deps_into_project(rustc_source_dir: &Path, project: &ClippyProjectInfo fn read_project_file(file_path: &str) -> Result { let path = Path::new(file_path); if !path.exists() { - eprintln!("error: unable to find the file `{}`", file_path); + eprintln!("error: unable to find the file `{file_path}`"); return Err(()); } match fs::read_to_string(path) { Ok(content) => Ok(content), Err(err) => { - eprintln!("error: the file `{}` could not be read ({})", file_path, err); + eprintln!("error: the file `{file_path}` could not be read ({err})"); Err(()) }, } @@ -124,10 +124,7 @@ fn inject_deps_into_manifest( ) -> std::io::Result<()> { // do not inject deps if we have already done so if cargo_toml.contains(RUSTC_PATH_SECTION) { - eprintln!( - "warn: dependencies are already setup inside {}, skipping file", - manifest_path - ); + eprintln!("warn: dependencies are already setup inside {manifest_path}, skipping file"); return Ok(()); } @@ -142,11 +139,7 @@ fn inject_deps_into_manifest( let new_deps = extern_crates.map(|dep| { // format the dependencies that are going to be put inside the Cargo.toml - format!( - "{dep} = {{ path = \"{source_path}/{dep}\" }}\n", - dep = dep, - source_path = rustc_source_dir.display() - ) + format!("{dep} = {{ path = \"{}/{dep}\" }}\n", rustc_source_dir.display()) }); // format a new [dependencies]-block with the new deps we need to inject @@ -163,11 +156,11 @@ fn inject_deps_into_manifest( // etc let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1); - // println!("{}", new_manifest); + // println!("{new_manifest}"); let mut file = File::create(manifest_path)?; file.write_all(new_manifest.as_bytes())?; - println!("info: successfully setup dependencies inside {}", manifest_path); + println!("info: successfully setup dependencies inside {manifest_path}"); Ok(()) } @@ -214,8 +207,8 @@ fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool { }, Err(err) => { eprintln!( - "error: unable to open file `{}` to remove rustc dependencies for {} ({})", - project.cargo_file, project.name, err + "error: unable to open file `{}` to remove rustc dependencies for {} ({err})", + project.cargo_file, project.name ); false }, diff --git a/clippy_dev/src/setup/vscode.rs b/clippy_dev/src/setup/vscode.rs index d59001b2c66a..dbcdc9b59e52 100644 --- a/clippy_dev/src/setup/vscode.rs +++ b/clippy_dev/src/setup/vscode.rs @@ -17,10 +17,7 @@ pub fn install_tasks(force_override: bool) { println!("info: the task file can be removed with `cargo dev remove vscode-tasks`"); println!("vscode tasks successfully installed"); }, - Err(err) => eprintln!( - "error: unable to copy `{}` to `{}` ({})", - TASK_SOURCE_FILE, TASK_TARGET_FILE, err - ), + Err(err) => eprintln!("error: unable to copy `{TASK_SOURCE_FILE}` to `{TASK_TARGET_FILE}` ({err})"), } } @@ -44,23 +41,17 @@ fn check_install_precondition(force_override: bool) -> bool { return delete_vs_task_file(path); } - eprintln!( - "error: there is already a `task.json` file inside the `{}` directory", - VSCODE_DIR - ); + eprintln!("error: there is already a `task.json` file inside the `{VSCODE_DIR}` directory"); println!("info: use the `--force-override` flag to override the existing `task.json` file"); return false; } } else { match fs::create_dir(vs_dir_path) { Ok(_) => { - println!("info: created `{}` directory for clippy", VSCODE_DIR); + println!("info: created `{VSCODE_DIR}` directory for clippy"); }, Err(err) => { - eprintln!( - "error: the task target directory `{}` could not be created ({})", - VSCODE_DIR, err - ); + eprintln!("error: the task target directory `{VSCODE_DIR}` could not be created ({err})"); }, } } @@ -82,7 +73,7 @@ pub fn remove_tasks() { fn delete_vs_task_file(path: &Path) -> bool { if let Err(err) = fs::remove_file(path) { - eprintln!("error: unable to delete the existing `tasks.json` file ({})", err); + eprintln!("error: unable to delete the existing `tasks.json` file ({err})"); return false; } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index b95061bf81a2..0eb443167ecf 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -45,9 +45,8 @@ fn generate_lint_files( renamed_lints: &[RenamedLint], ) { let internal_lints = Lint::internal_lints(lints); - let usable_lints = Lint::usable_lints(lints); - let mut sorted_usable_lints = usable_lints.clone(); - sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); + let mut usable_lints = Lint::usable_lints(lints); + usable_lints.sort_by_key(|lint| lint.name.clone()); replace_region_in_file( update_mode, @@ -86,7 +85,7 @@ fn generate_lint_files( ) .sorted() { - writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap(); + writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); } }, ); @@ -99,7 +98,7 @@ fn generate_lint_files( "// end lints modules, do not remove this comment, it’s used in `update_lints`", |res| { for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() { - writeln!(res, "mod {};", lint_mod).unwrap(); + writeln!(res, "mod {lint_mod};").unwrap(); } }, ); @@ -129,7 +128,7 @@ fn generate_lint_files( for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { let content = gen_lint_group_list(&lint_group, lints.iter()); process_file( - &format!("clippy_lints/src/lib.register_{}.rs", lint_group), + &format!("clippy_lints/src/lib.register_{lint_group}.rs"), update_mode, &content, ); @@ -190,9 +189,9 @@ fn print_lint_names(header: &str, lints: &BTreeSet) -> bool { if lints.is_empty() { return false; } - println!("{}", header); + println!("{header}"); for lint in lints.iter().sorted() { - println!(" {}", lint); + println!(" {lint}"); } println!(); true @@ -205,16 +204,16 @@ pub fn print_lints() { let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); for (lint_group, mut lints) in grouped_by_lint_group { - println!("\n## {}", lint_group); + println!("\n## {lint_group}"); lints.sort_by_key(|l| l.name.clone()); for lint in lints { - println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc); + println!("* [{}]({DOCS_LINK}#{}) ({})", lint.name, lint.name, lint.desc); } } - println!("there are {} lints", usable_lint_count); + println!("there are {usable_lint_count} lints"); } /// Runs the `rename_lint` command. @@ -235,10 +234,10 @@ pub fn print_lints() { #[allow(clippy::too_many_lines)] pub fn rename(old_name: &str, new_name: &str, uplift: bool) { if let Some((prefix, _)) = old_name.split_once("::") { - panic!("`{}` should not contain the `{}` prefix", old_name, prefix); + panic!("`{old_name}` should not contain the `{prefix}` prefix"); } if let Some((prefix, _)) = new_name.split_once("::") { - panic!("`{}` should not contain the `{}` prefix", new_name, prefix); + panic!("`{new_name}` should not contain the `{prefix}` prefix"); } let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); @@ -251,14 +250,14 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { found_new_name = true; } } - let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name)); + let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`")); let lint = RenamedLint { - old_name: format!("clippy::{}", old_name), + old_name: format!("clippy::{old_name}"), new_name: if uplift { new_name.into() } else { - format!("clippy::{}", new_name) + format!("clippy::{new_name}") }, }; @@ -266,13 +265,11 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { // case. assert!( !renamed_lints.iter().any(|l| lint.old_name == l.old_name), - "`{}` has already been renamed", - old_name + "`{old_name}` has already been renamed" ); assert!( !deprecated_lints.iter().any(|l| lint.old_name == l.name), - "`{}` has already been deprecated", - old_name + "`{old_name}` has already been deprecated" ); // Update all lint level attributes. (`clippy::lint_name`) @@ -309,14 +306,12 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { if uplift { write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); println!( - "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.", - old_name + "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually." ); } else if found_new_name { write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); println!( - "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.", - new_name + "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually." ); } else { // Rename the lint struct and source files sharing a name with the lint. @@ -327,16 +322,16 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. if try_rename_file( - Path::new(&format!("tests/ui/{}.rs", old_name)), - Path::new(&format!("tests/ui/{}.rs", new_name)), + Path::new(&format!("tests/ui/{old_name}.rs")), + Path::new(&format!("tests/ui/{new_name}.rs")), ) { try_rename_file( - Path::new(&format!("tests/ui/{}.stderr", old_name)), - Path::new(&format!("tests/ui/{}.stderr", new_name)), + Path::new(&format!("tests/ui/{old_name}.stderr")), + Path::new(&format!("tests/ui/{new_name}.stderr")), ); try_rename_file( - Path::new(&format!("tests/ui/{}.fixed", old_name)), - Path::new(&format!("tests/ui/{}.fixed", new_name)), + Path::new(&format!("tests/ui/{old_name}.fixed")), + Path::new(&format!("tests/ui/{new_name}.fixed")), ); } @@ -344,8 +339,8 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { let replacements; let replacements = if lint.module == old_name && try_rename_file( - Path::new(&format!("clippy_lints/src/{}.rs", old_name)), - Path::new(&format!("clippy_lints/src/{}.rs", new_name)), + Path::new(&format!("clippy_lints/src/{old_name}.rs")), + Path::new(&format!("clippy_lints/src/{new_name}.rs")), ) { // Edit the module name in the lint list. Note there could be multiple lints. for lint in lints.iter_mut().filter(|l| l.module == old_name) { @@ -356,14 +351,14 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { } else if !lint.module.contains("::") // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` && try_rename_file( - Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)), - Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)), + Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)), + Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)), ) { // Edit the module name in the lint list. Note there could be multiple lints, or none. - let renamed_mod = format!("{}::{}", lint.module, old_name); + let renamed_mod = format!("{}::{old_name}", lint.module); for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { - lint.module = format!("{}::{}", lint.module, new_name); + lint.module = format!("{}::{new_name}", lint.module); } replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; replacements.as_slice() @@ -379,7 +374,7 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { } generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("{} has been successfully renamed", old_name); + println!("{old_name} has been successfully renamed"); } println!("note: `cargo uitest` still needs to be run to update the test results"); @@ -408,7 +403,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) { }); generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{}` has successfully been deprecated", name); + println!("info: `{name}` has successfully been deprecated"); if reason == DEFAULT_DEPRECATION_REASON { println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`"); @@ -421,7 +416,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) { let name_upper = name.to_uppercase(); let (mut lints, deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{}`", name); return; }; + let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{name}`"); return; }; let mod_path = { let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); @@ -450,7 +445,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io } fn remove_test_assets(name: &str) { - let test_file_stem = format!("tests/ui/{}", name); + let test_file_stem = format!("tests/ui/{name}"); let path = Path::new(&test_file_stem); // Some lints have their own directories, delete them @@ -512,8 +507,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); eprintln!( - "warn: you will have to manually remove any code related to `{}` from `{}`", - name, + "warn: you will have to manually remove any code related to `{name}` from `{}`", path.display() ); @@ -528,7 +522,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io content.replace_range(lint.declaration_range.clone(), ""); // Remove the module declaration (mod xyz;) - let mod_decl = format!("\nmod {};", name); + let mod_decl = format!("\nmod {name};"); content = content.replacen(&mod_decl, "", 1); remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); @@ -621,13 +615,13 @@ fn round_to_fifty(count: usize) -> usize { fn process_file(path: impl AsRef, update_mode: UpdateMode, content: &str) { if update_mode == UpdateMode::Check { let old_content = - fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.as_ref().display(), e)); + fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {e}", path.as_ref().display())); if content != old_content { exit_with_failure(); } } else { fs::write(&path, content.as_bytes()) - .unwrap_or_else(|e| panic!("Cannot write to {}: {}", path.as_ref().display(), e)); + .unwrap_or_else(|e| panic!("Cannot write to {}: {e}", path.as_ref().display())); } } @@ -731,11 +725,10 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator( if !is_public { output.push_str(" #[cfg(feature = \"internal\")]\n"); } - let _ = writeln!(output, " {}::{},", module_name, lint_name); + let _ = writeln!(output, " {module_name}::{lint_name},"); } output.push_str("])\n"); @@ -841,7 +834,7 @@ fn gather_all() -> (Vec, Vec, Vec) { for (rel_path, file) in clippy_lints_src_files() { let path = file.path(); let contents = - fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); let module = rel_path .components() .map(|c| c.as_os_str().to_str().unwrap()) @@ -1050,7 +1043,7 @@ fn remove_line_splices(s: &str) -> String { .trim_matches('#') .strip_prefix('"') .and_then(|s| s.strip_suffix('"')) - .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s)); + .unwrap_or_else(|| panic!("expected quoted string, found `{s}`")); let mut res = String::with_capacity(s.len()); unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| { if ch.is_ok() { @@ -1076,10 +1069,10 @@ fn replace_region_in_file( end: &str, write_replacement: impl FnMut(&mut String), ) { - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) { Ok(x) => x, - Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()), + Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()), }; match update_mode { @@ -1087,7 +1080,7 @@ fn replace_region_in_file( UpdateMode::Check => (), UpdateMode::Change => { if let Err(e) = fs::write(path, new_contents.as_bytes()) { - panic!("Cannot write to `{}`: {}", path.display(), e); + panic!("Cannot write to `{}`: {e}", path.display()); } }, } @@ -1135,7 +1128,7 @@ fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { #[allow(clippy::needless_pass_by_value)] fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { - panic!("failed to {} file `{}`: {}", action, name.display(), error) + panic!("failed to {action} file `{}`: {error}", name.display()) } fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 738562ef8559..6fbd6401ef3e 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.65" +version = "0.1.66" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 159f3b0cd014..724490fb4959 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -92,7 +92,7 @@ impl ApproxConstant { cx, APPROX_CONSTANT, e.span, - &format!("approximate value of `{}::consts::{}` found", module, &name), + &format!("approximate value of `{module}::consts::{}` found", &name), None, "consider using the constant directly", ); @@ -126,7 +126,7 @@ fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool { // The value is a truncated constant true } else { - let round_const = format!("{:.*}", value.len() - 2, constant); + let round_const = format!("{constant:.*}", value.len() - 2); value == round_const } } diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs index f419781dbc82..9717aa9e981f 100644 --- a/clippy_lints/src/asm_syntax.rs +++ b/clippy_lints/src/asm_syntax.rs @@ -44,7 +44,7 @@ fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr cx, lint, expr.span, - &format!("{} x86 assembly syntax used", style), + &format!("{style} x86 assembly syntax used"), None, &format!("use {} x86 assembly syntax", !style), ); @@ -64,6 +64,7 @@ declare_clippy_lint! { /// /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); @@ -72,6 +73,7 @@ declare_clippy_lint! { /// Use instead: /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); @@ -103,6 +105,7 @@ declare_clippy_lint! { /// /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); @@ -111,6 +114,7 @@ declare_clippy_lint! { /// Use instead: /// ```rust,no_run /// # #![feature(asm)] + /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 2705ffffdcbf..a36df55d0bda 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -60,9 +60,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { cx, ASSERTIONS_ON_CONSTANTS, macro_call.span, - &format!("`assert!(false{})` should probably be replaced", assert_arg), + &format!("`assert!(false{assert_arg})` should probably be replaced"), None, - &format!("use `panic!({})` or `unreachable!({0})`", panic_arg), + &format!("use `panic!({panic_arg})` or `unreachable!({panic_arg})`"), ); } } diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 656dc5feeb57..f6d6c23bb6ed 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -69,9 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { "called `assert!` with `Result::is_ok`", "replace with", format!( - "{}.unwrap(){}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0, - semicolon + "{}.unwrap(){semicolon}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 ), app, ); @@ -84,9 +83,8 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { "called `assert!` with `Result::is_err`", "replace with", format!( - "{}.unwrap_err(){}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0, - semicolon + "{}.unwrap_err(){semicolon}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 ), app, ); diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 732dc2b43309..5f45c69d7f98 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -541,10 +541,7 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut cx, INLINE_ALWAYS, attr.span, - &format!( - "you have declared `#[inline(always)]` on `{}`. This is usually a bad idea", - name - ), + &format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), ); } } @@ -720,7 +717,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { let mut unix_suggested = false; for (os, span) in mismatched { - let sugg = format!("target_os = \"{}\"", os); + let sugg = format!("target_os = \"{os}\""); diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); if !unix_suggested && is_unix(os) { diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 1761360fb281..34717811866d 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,14 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::GeneratorInteriorTypeCause; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; +use rustc_span::{sym, Span}; -use crate::utils::conf::DisallowedType; +use crate::utils::conf::DisallowedPath; declare_clippy_lint! { /// ### What it does @@ -171,12 +172,12 @@ impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, #[derive(Debug)] pub struct AwaitHolding { - conf_invalid_types: Vec, - def_ids: FxHashMap, + conf_invalid_types: Vec, + def_ids: FxHashMap, } impl AwaitHolding { - pub(crate) fn new(conf_invalid_types: Vec) -> Self { + pub(crate) fn new(conf_invalid_types: Vec) -> Self { Self { conf_invalid_types, def_ids: FxHashMap::default(), @@ -187,11 +188,8 @@ impl AwaitHolding { impl LateLintPass<'_> for AwaitHolding { fn check_crate(&mut self, cx: &LateContext<'_>) { for conf in &self.conf_invalid_types { - let path = match conf { - DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path, - }; - let segs: Vec<_> = path.split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { + let segs: Vec<_> = conf.path().split("::").collect(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { self.def_ids.insert(id, conf.clone()); } } @@ -256,29 +254,27 @@ impl AwaitHolding { } } -fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) { - let (type_name, reason) = match disallowed { - DisallowedType::Simple(path) => (path, &None), - DisallowedType::WithReason { path, reason } => (path, reason), - }; - +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPath) { span_lint_and_then( cx, AWAIT_HOLDING_INVALID_TYPE, span, - &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",), + &format!( + "`{}` may not be held across an `await` point per `clippy.toml`", + disallowed.path() + ), |diag| { - if let Some(reason) = reason { - diag.note(reason.clone()); + if let Some(reason) = disallowed.reason() { + diag.note(reason); } }, ); } fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) + cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id) + || cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id) + || cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id) || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index d9e2c9c8578f..9c0532474024 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -3,10 +3,11 @@ use clippy_utils::get_parent_expr; use clippy_utils::higher; use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::{for_each_expr, Descend}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{BlockCheckMode, Closure, Expr, ExprKind}; +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}; @@ -44,39 +45,6 @@ declare_clippy_lint! { declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]); -struct ExVisitor<'a, 'tcx> { - found_block: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let ExprKind::Closure(&Closure { body, .. }) = expr.kind { - // do not lint if the closure is called using an iterator (see #1141) - if_chain! { - if let Some(parent) = get_parent_expr(self.cx, expr); - if let ExprKind::MethodCall(_, self_arg, ..) = &parent.kind; - let caller = self.cx.typeck_results().expr_ty(self_arg); - if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(self.cx, caller, iter_id, &[]); - then { - return; - } - } - - let body = self.cx.tcx.hir().body(body); - let ex = &body.value; - if let ExprKind::Block(block, _) = ex.kind { - if !body.value.span.from_expansion() && !block.stmts.is_empty() { - self.found_block = Some(ex); - return; - } - } - } - walk_expr(self, expr); - } -} - const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ instead, move the block or closure higher and bind it with a `let`"; @@ -145,11 +113,31 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions { } } } else { - let mut visitor = ExVisitor { found_block: None, cx }; - walk_expr(&mut visitor, cond); - if let Some(block) = visitor.found_block { - span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE); - } + let _: Option = for_each_expr(cond, |e| { + if let ExprKind::Closure(closure) = e.kind { + // do not lint if the closure is called using an iterator (see #1141) + if_chain! { + if let Some(parent) = get_parent_expr(cx, e); + if let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind; + let caller = cx.typeck_results().expr_ty(self_arg); + if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + if implements_trait(cx, caller, iter_id, &[]); + then { + return ControlFlow::Continue(Descend::No); + } + } + + let body = cx.tcx.hir().body(closure.body); + let ex = &body.value; + if let ExprKind::Block(block, _) = ex.kind { + if !body.value.span.from_expansion() && !block.stmts.is_empty() { + span_lint(cx, BLOCKS_IN_IF_CONDITIONS, ex.span, COMPLEX_BLOCK_MESSAGE); + return ControlFlow::Continue(Descend::No); + } + } + } + ControlFlow::Continue(Descend::Yes) + }); } } } diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 95abe8aa59fb..4bd55c1429c3 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -98,9 +98,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { cx, BOOL_ASSERT_COMPARISON, macro_call.span, - &format!("used `{}!` with a literal bool", macro_name), + &format!("used `{macro_name}!` with a literal bool"), "replace it with", - format!("{}!(..)", non_eq_mac), + format!("{non_eq_mac}!(..)"), Applicability::MaybeIncorrect, ); } diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 51e98cda8451..001d74c26054 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -3,7 +3,7 @@ use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, sugg::Sugg}; +use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, is_integer_literal, sugg::Sugg}; use rustc_errors::Applicability; declare_clippy_lint! { @@ -56,13 +56,9 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx && let Some(then_lit) = int_literal(then) && let Some(else_lit) = int_literal(else_) { - let inverted = if - check_int_literal_equals_val(then_lit, 1) - && check_int_literal_equals_val(else_lit, 0) { + let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) { false - } else if - check_int_literal_equals_val(then_lit, 0) - && check_int_literal_equals_val(else_lit, 1) { + } else if is_integer_literal(then_lit, 0) && is_integer_literal(else_lit, 1) { true } else { // Expression isn't boolean, exit @@ -123,14 +119,3 @@ fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hi None } } - -fn check_int_literal_equals_val<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>, expected_value: u128) -> bool { - if let ExprKind::Lit(lit) = &expr.kind - && let LitKind::Int(val, _) = lit.node - && val == expected_value - { - true - } else { - false - } -} diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 03d262d5a59c..2a15cbc7a3c3 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -263,9 +263,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } .and_then(|op| { Some(format!( - "{}{}{}", + "{}{op}{}", snippet_opt(cx, lhs.span)?, - op, snippet_opt(cx, rhs.span)? )) }) @@ -285,7 +284,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let path: &str = path.ident.name.as_str(); a == path }) - .and_then(|(_, neg_method)| Some(format!("{}.{}()", snippet_opt(cx, receiver.span)?, neg_method))) + .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", snippet_opt(cx, receiver.span)?))) }, _ => None, } diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs new file mode 100644 index 000000000000..792183ac4081 --- /dev/null +++ b/clippy_lints/src/box_default.rs @@ -0,0 +1,61 @@ +use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id}; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// checks for `Box::new(T::default())`, which is better written as + /// `Box::::default()`. + /// + /// ### Why is this bad? + /// First, it's more complex, involving two calls instead of one. + /// Second, `Box::default()` can be faster + /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). + /// + /// ### Known problems + /// The lint may miss some cases (e.g. Box::new(String::from(""))). + /// On the other hand, it will trigger on cases where the `default` + /// code comes from a macro that does something different based on + /// e.g. target operating system. + /// + /// ### Example + /// ```rust + /// let x: Box = Box::new(Default::default()); + /// ``` + /// Use instead: + /// ```rust + /// let x: Box = Box::default(); + /// ``` + #[clippy::version = "1.65.0"] + pub BOX_DEFAULT, + perf, + "Using Box::new(T::default()) instead of Box::default()" +} + +declare_lint_pass!(BoxDefault => [BOX_DEFAULT]); + +impl LateLintPass<'_> for BoxDefault { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Call(box_new, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind + && let ExprKind::Call(..) = arg.kind + && !in_external_macro(cx.sess(), expr.span) + && expr.span.eq_ctxt(arg.span) + && seg.ident.name == sym::new + && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box() + && is_default_equivalent(cx, arg) + { + span_lint_and_help( + cx, + BOX_DEFAULT, + expr.span, + "`Box::new(_)` of default value", + None, + "use `Box::default()` instead", + ); + } + } +} diff --git a/clippy_lints/src/cargo/common_metadata.rs b/clippy_lints/src/cargo/common_metadata.rs index e0442dda479d..805121bcced3 100644 --- a/clippy_lints/src/cargo/common_metadata.rs +++ b/clippy_lints/src/cargo/common_metadata.rs @@ -40,7 +40,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b } fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) { - let message = format!("package `{}` is missing `{}` metadata", package.name, field); + let message = format!("package `{}` is missing `{field}` metadata", package.name); span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message); } diff --git a/clippy_lints/src/cargo/feature_name.rs b/clippy_lints/src/cargo/feature_name.rs index 79a469a4258b..37c169dbd95e 100644 --- a/clippy_lints/src/cargo/feature_name.rs +++ b/clippy_lints/src/cargo/feature_name.rs @@ -57,10 +57,8 @@ fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) { }, DUMMY_SP, &format!( - "the \"{}\" {} in the feature name \"{}\" is {}", - substring, + "the \"{substring}\" {} in the feature name \"{feature}\" is {}", if is_prefix { "prefix" } else { "suffix" }, - feature, if is_negative { "negative" } else { "redundant" } ), None, diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index 9f45db86a091..3a872e54c9a2 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -196,7 +196,7 @@ impl LateLintPass<'_> for Cargo { }, Err(e) => { for lint in NO_DEPS_LINTS { - span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {}", e)); + span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {e}")); } }, } @@ -212,7 +212,7 @@ impl LateLintPass<'_> for Cargo { }, Err(e) => { for lint in WITH_DEPS_LINTS { - span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {}", e)); + span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {e}")); } }, } diff --git a/clippy_lints/src/cargo/multiple_crate_versions.rs b/clippy_lints/src/cargo/multiple_crate_versions.rs index 76fd0819a39a..f9b17d45e9fb 100644 --- a/clippy_lints/src/cargo/multiple_crate_versions.rs +++ b/clippy_lints/src/cargo/multiple_crate_versions.rs @@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, - &format!("multiple versions for dependency `{}`: {}", name, versions), + &format!("multiple versions for dependency `{name}`: {versions}"), ); } } diff --git a/clippy_lints/src/casts/borrow_as_ptr.rs b/clippy_lints/src/casts/borrow_as_ptr.rs index 6e1f8cd64f07..294d22d34de9 100644 --- a/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/clippy_lints/src/casts/borrow_as_ptr.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( expr.span, "borrow as raw pointer", "try", - format!("{}::ptr::{}!({})", core_or_std, macro_name, snip), + format!("{core_or_std}::ptr::{macro_name}!({snip})"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 938458e30cad..13c403234dad 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -41,15 +41,9 @@ pub(super) fn check( ); let message = if cast_from.is_bool() { - format!( - "casting `{0:}` to `{1:}` is more cleanly stated with `{1:}::from(_)`", - cast_from, cast_to - ) + format!("casting `{cast_from:}` to `{cast_to:}` is more cleanly stated with `{cast_to:}::from(_)`") } else { - format!( - "casting `{}` to `{}` may become silently lossy if you later change the type", - cast_from, cast_to - ) + format!("casting `{cast_from}` to `{cast_to}` may become silently lossy if you later change the type") }; span_lint_and_sugg( @@ -58,7 +52,7 @@ pub(super) fn check( expr.span, &message, "try", - format!("{}::from({})", cast_to, sugg), + format!("{cast_to}::from({sugg})"), applicability, ); } diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 406547a4454e..88deb4565eb2 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -103,10 +103,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, return; } - format!( - "casting `{}` to `{}` may truncate the value{}", - cast_from, cast_to, suffix, - ) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) }, (ty::Adt(def, _), true) if def.is_enum() => { @@ -142,20 +139,17 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, CAST_ENUM_TRUNCATION, expr.span, &format!( - "casting `{}::{}` to `{}` will truncate the value{}", - cast_from, variant.name, cast_to, suffix, + "casting `{cast_from}::{}` to `{cast_to}` will truncate the value{suffix}", + variant.name, ), ); return; } - format!( - "casting `{}` to `{}` may truncate the value{}", - cast_from, cast_to, suffix, - ) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) }, (ty::Float(_), true) => { - format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value") }, (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => { diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index 2c5c1d7cb465..28ecdea7ea06 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -35,10 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca cx, CAST_POSSIBLE_WRAP, expr.span, - &format!( - "casting `{}` to `{}` may wrap around the value{}", - cast_from, cast_to, suffix, - ), + &format!("casting `{cast_from}` to `{cast_to}` may wrap around the value{suffix}",), ); } } diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index da7b12f67266..97054a0d1015 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -49,9 +49,7 @@ fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_f CAST_PTR_ALIGNMENT, expr.span, &format!( - "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", - cast_from, - cast_to, + "casting from `{cast_from}` to a more-strictly-aligned pointer (`{cast_to}`) ({} < {} bytes)", from_layout.align.abi.bytes(), to_layout.align.abi.bytes(), ), diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 5b59350be042..a20a97d4e56d 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -14,10 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, c cx, CAST_SIGN_LOSS, expr.span, - &format!( - "casting `{}` to `{}` may lose the sign of the value", - cast_from, cast_to - ), + &format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), ); } } diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 027c660ce3b2..d31d10d22b92 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -35,8 +35,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Optio CAST_SLICE_DIFFERENT_SIZES, expr.span, &format!( - "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count", - start_ty.ty, from_size, end_ty.ty, to_size, + "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count", + start_ty.ty, end_ty.ty, ), |diag| { let ptr_snippet = source::snippet(cx, left_cast.span, ".."); diff --git a/clippy_lints/src/casts/char_lit_as_u8.rs b/clippy_lints/src/casts/char_lit_as_u8.rs index 7cc406018dbe..82e07c98a7e0 100644 --- a/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/clippy_lints/src/casts/char_lit_as_u8.rs @@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { diag.span_suggestion( expr.span, "use a byte literal instead", - format!("b{}", snippet), + format!("b{snippet}"), applicability, ); } diff --git a/clippy_lints/src/casts/fn_to_numeric_cast.rs b/clippy_lints/src/casts/fn_to_numeric_cast.rs index 35350d8a25b8..a26bfab4e7c1 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -25,9 +25,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST, expr.span, - &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), + &format!("casting function pointer `{from_snippet}` to `{cast_to}`"), "try", - format!("{} as usize", from_snippet), + format!("{from_snippet} as usize"), applicability, ); } diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs index 03621887a34a..75654129408e 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -23,9 +23,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST_ANY, expr.span, - &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), + &format!("casting function pointer `{from_snippet}` to `{cast_to}`"), "did you mean to invoke the function?", - format!("{}() as {}", from_snippet, cast_to), + format!("{from_snippet}() as {cast_to}"), applicability, ); }, diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index 6287f479b5bf..556be1d15066 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -24,12 +24,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cx, FN_TO_NUMERIC_CAST_WITH_TRUNCATION, expr.span, - &format!( - "casting function pointer `{}` to `{}`, which truncates the value", - from_snippet, cast_to - ), + &format!("casting function pointer `{from_snippet}` to `{cast_to}`, which truncates the value"), "try", - format!("{} as usize", from_snippet), + format!("{from_snippet} as usize"), applicability, ); } diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 46d45d09661a..c2b9253ec35d 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -33,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option Cow::Borrowed(""), TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), - _ => Cow::Owned(format!("::<{}>", to_pointee_ty)), + _ => Cow::Owned(format!("::<{to_pointee_ty}>")), }; span_lint_and_sugg( cx, @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option( } } + let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); + if let Some(lit) = get_numeric_literal(cast_expr) { - let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); + let literal_str = &cast_str; if_chain! { if let LitKind::Int(n, _) = lit.node; @@ -49,12 +52,16 @@ pub(super) fn check<'tcx>( match lit.node { LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => { - lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); + lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); + return false; }, LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { - lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); + lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); + return false; + }, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => { + return false; }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => @@ -62,48 +69,62 @@ pub(super) fn check<'tcx>( if let Some(src) = snippet_opt(cx, cast_expr.span) { if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + return true; } } }, - _ => { - if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { - span_lint_and_sugg( - cx, - UNNECESSARY_CAST, - expr.span, - &format!( - "casting to the same type is unnecessary (`{}` -> `{}`)", - cast_from, cast_to - ), - "try", - literal_str, - Applicability::MachineApplicable, - ); - return true; - } - }, + _ => {}, } } + if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), + "try", + cast_str, + Applicability::MachineApplicable, + ); + return true; + } + false } -fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { +fn lint_unnecessary_cast( + cx: &LateContext<'_>, + expr: &Expr<'_>, + raw_literal_str: &str, + cast_from: Ty<'_>, + cast_to: Ty<'_>, +) { let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; - let replaced_literal; - let matchless = if literal_str.contains(['(', ')']) { - replaced_literal = literal_str.replace(['(', ')'], ""); - &replaced_literal - } else { - literal_str + // first we remove all matches so `-(1)` become `-1`, and remove trailing dots, so `1.` become `1` + let literal_str = raw_literal_str + .replace(['(', ')'], "") + .trim_end_matches('.') + .to_string(); + // we know need to check if the parent is a method call, to add parenthesis accordingly (eg: + // (-1).foo() instead of -1.foo()) + let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(..) = parent_expr.kind + && literal_str.starts_with('-') + { + format!("({literal_str}_{cast_to})") + + } else { + format!("{literal_str}_{cast_to}") }; + span_lint_and_sugg( cx, UNNECESSARY_CAST, expr.span, - &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), + &format!("casting {literal_kind_name} literal to `{cast_to}` is unnecessary"), "try", - format!("{}_{}", matchless.trim_end_matches('.'), cast_to), + sugg, Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 37b2fdcff09f..78e9921f036f 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -2,9 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, meets_msrv, msrvs, SpanlessEq}; +use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -82,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { item.span, "checked cast can be simplified", "try", - format!("{}::try_from({}).is_ok()", to_type, snippet), + format!("{to_type}::try_from({snippet}).is_ok()"), applicability, ); } @@ -223,16 +222,7 @@ fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { /// Check for `expr >= 0` fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option> { - if_chain! { - if let ExprKind::Lit(ref lit) = &check.kind; - if let LitKind::Int(0, _) = &lit.node; - - then { - Some(Conversion::new_any(candidate)) - } else { - None - } - } + is_integer_literal(check, 0).then(|| Conversion::new_any(candidate)) } /// Check for `expr >= (to_type::MIN as from_type)` diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 33c44f8b2dba..77af3b53d633 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -3,10 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr; use clippy_utils::LimitStack; +use core::ops::ControlFlow; use rustc_ast::ast::Attribute; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -61,11 +63,27 @@ impl CognitiveComplexity { return; } - let expr = &body.value; + let expr = body.value; + + let mut cc = 1u64; + let mut returns = 0u64; + let _: Option = for_each_expr(expr, |e| { + match e.kind { + ExprKind::If(_, _, _) => { + cc += 1; + }, + ExprKind::Match(_, arms, _) => { + if arms.len() > 1 { + cc += 1; + } + cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; + }, + ExprKind::Ret(_) => returns += 1, + _ => {}, + } + ControlFlow::Continue(()) + }); - let mut helper = CcHelper { cc: 1, returns: 0 }; - helper.visit_expr(expr); - let CcHelper { cc, returns } = helper; let ret_ty = cx.typeck_results().node_type(expr.hir_id); let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { returns @@ -74,13 +92,12 @@ impl CognitiveComplexity { (returns / 2) }; - let mut rust_cc = cc; // prevent degenerate cases where unreachable code contains `return` statements - if rust_cc >= ret_adjust { - rust_cc -= ret_adjust; + if cc >= ret_adjust { + cc -= ret_adjust; } - if rust_cc > self.limit.limit() { + if cc > self.limit.limit() { let fn_span = match kind { FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span, FnKind::Closure => { @@ -107,8 +124,7 @@ impl CognitiveComplexity { COGNITIVE_COMPLEXITY, fn_span, &format!( - "the function has a cognitive complexity of ({}/{})", - rust_cc, + "the function has a cognitive complexity of ({cc}/{})", self.limit.limit() ), None, @@ -141,27 +157,3 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity"); } } - -struct CcHelper { - cc: u64, - returns: u64, -} - -impl<'tcx> Visitor<'tcx> for CcHelper { - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - walk_expr(self, e); - match e.kind { - ExprKind::If(_, _, _) => { - self.cc += 1; - }, - ExprKind::Match(_, arms, _) => { - if arms.len() > 1 { - self.cc += 1; - } - self.cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; - }, - ExprKind::Ret(_) => self.returns += 1, - _ => {}, - } - } -} diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 4e68d6810e29..7f937de1dd31 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { cx, DEFAULT_TRAIT_ACCESS, expr.span, - &format!("calling `{}` is more clear than this expression", replacement), + &format!("calling `{replacement}` is more clear than this expression"), "try", replacement, Applicability::Unspecified, // First resolve the TODO above @@ -210,7 +210,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .map(|(field, rhs)| { // extract and store the assigned value for help message let value_snippet = snippet_with_macro_callsite(cx, rhs.span, ".."); - format!("{}: {}", field, value_snippet) + format!("{field}: {value_snippet}") }) .collect::>() .join(", "); @@ -227,7 +227,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .map(ToString::to_string) .collect::>() .join(", "); - format!("{}::<{}>", adt_def_ty_name, &tys_str) + format!("{adt_def_ty_name}::<{}>", &tys_str) } else { binding_type.to_string() } @@ -235,12 +235,12 @@ impl<'tcx> LateLintPass<'tcx> for Default { let sugg = if ext_with_default { if field_list.is_empty() { - format!("{}::default()", binding_type) + format!("{binding_type}::default()") } else { - format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) + format!("{binding_type} {{ {field_list}, ..Default::default() }}") } } else { - format!("{} {{ {} }}", binding_type, field_list) + format!("{binding_type} {{ {field_list} }}") }; // span lint once per statement that binds default @@ -250,10 +250,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { first_assign.unwrap().span, "field assignment outside of initializer for an instance created with Default::default()", Some(local.span), - &format!( - "consider initializing the variable with `{}` and removing relevant reassignments", - sugg - ), + &format!("consider initializing the variable with `{sugg}` and removing relevant reassignments"), ); self.reassigned_linted.insert(span); } diff --git a/clippy_lints/src/default_instead_of_iter_empty.rs b/clippy_lints/src/default_instead_of_iter_empty.rs index 3c996d3d2aee..1ad929864b2a 100644 --- a/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/clippy_lints/src/default_instead_of_iter_empty.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// let _ = std::iter::empty::(); /// let iter: std::iter::Empty = std::iter::empty(); /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.64.0"] pub DEFAULT_INSTEAD_OF_ITER_EMPTY, style, "check `std::iter::Empty::default()` and replace with `std::iter::empty()`" diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index be02f328e989..3ed9cd36a229 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -95,8 +95,8 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { src } else { match lit.node { - LitKind::Int(src, _) => format!("{}", src), - LitKind::Float(src, _) => format!("{}", src), + LitKind::Int(src, _) => format!("{src}"), + LitKind::Float(src, _) => format!("{src}"), _ => return, } }; diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 3905a6c2e211..741edc131960 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{self as hir, HirId, Item, ItemKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index f0d5ed6f594b..3cd8f236e7a5 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -135,7 +135,7 @@ declare_clippy_lint! { /// let x = String::new(); /// let y: &str = &x; /// ``` - #[clippy::version = "1.60.0"] + #[clippy::version = "1.64.0"] pub EXPLICIT_AUTO_DEREF, complexity, "dereferencing when the compiler would automatically dereference" @@ -184,6 +184,7 @@ impl Dereferencing { } } +#[derive(Debug)] struct StateData { /// Span of the top level expression span: Span, @@ -191,12 +192,14 @@ struct StateData { position: Position, } +#[derive(Debug)] struct DerefedBorrow { count: usize, msg: &'static str, snip_expr: Option, } +#[derive(Debug)] enum State { // Any number of deref method calls. DerefMethod { @@ -276,10 +279,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { (None, kind) => { let expr_ty = typeck.expr_ty(expr); let (position, adjustments) = walk_parents(cx, expr, self.msrv); - match kind { RefOp::Deref => { - if let Position::FieldAccess(name) = position + if let Position::FieldAccess { + name, + of_union: false, + } = position && !ty_contains_field(typeck.expr_ty(sub_expr), name) { self.state = Some(( @@ -451,7 +456,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => { let position = data.position; report(cx, expr, State::DerefedBorrow(state), data); - if let Position::FieldAccess(name) = position + if let Position::FieldAccess{name, ..} = position && !ty_contains_field(typeck.expr_ty(sub_expr), name) { self.state = Some(( @@ -616,14 +621,17 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { } /// The position of an expression relative to it's parent. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] enum Position { MethodReceiver, /// The method is defined on a reference type. e.g. `impl Foo for &T` MethodReceiverRefImpl, Callee, ImplArg(HirId), - FieldAccess(Symbol), + FieldAccess { + name: Symbol, + of_union: bool, + }, // union fields cannot be auto borrowed Postfix, Deref, /// Any other location which will trigger auto-deref to a specific time. @@ -645,7 +653,10 @@ impl Position { } fn can_auto_borrow(self) -> bool { - matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee) + matches!( + self, + Self::MethodReceiver | Self::FieldAccess { of_union: false, .. } | Self::Callee + ) } fn lint_explicit_deref(self) -> bool { @@ -657,7 +668,7 @@ impl Position { Self::MethodReceiver | Self::MethodReceiverRefImpl | Self::Callee - | Self::FieldAccess(_) + | Self::FieldAccess { .. } | Self::Postfix => PREC_POSTFIX, Self::ImplArg(_) | Self::Deref => PREC_PREFIX, Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p, @@ -844,7 +855,10 @@ fn walk_parents<'tcx>( } }) }, - ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)), + ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess { + name: name.name, + of_union: is_union(cx.typeck_results(), child), + }), ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref), ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) | ExprKind::Index(child, _) @@ -865,6 +879,13 @@ fn walk_parents<'tcx>( (position, adjustments) } +fn is_union<'tcx>(typeck: &'tcx TypeckResults<'_>, path_expr: &'tcx Expr<'_>) -> bool { + typeck + .expr_ty_adjusted(path_expr) + .ty_adt_def() + .map_or(false, rustc_middle::ty::AdtDef::is_union) +} + fn closure_result_position<'tcx>( cx: &LateContext<'tcx>, closure: &'tcx Closure<'_>, @@ -1308,7 +1329,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data }; let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX { - format!("({})", expr_str) + format!("({expr_str})") } else { expr_str.into_owned() }; @@ -1322,7 +1343,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data Mutability::Mut => "explicit `deref_mut` method call", }, "try this", - format!("{}{}{}", addr_of_str, deref_str, expr_str), + format!("{addr_of_str}{deref_str}{expr_str}"), app, ); }, @@ -1336,7 +1357,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data && !has_enclosing_paren(&snip) && (expr.precedence().order() < data.position.precedence() || calls_field) { - format!("({})", snip) + format!("({snip})") } else { snip.into() }; @@ -1379,9 +1400,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); let sugg = if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) { - format!("{}({})", prefix, snip) + format!("{prefix}({snip})") } else { - format!("{}{}", prefix, snip) + format!("{prefix}{snip}") }; diag.span_suggestion(data.span, "try this", sugg, app); }, @@ -1460,14 +1481,14 @@ impl Dereferencing { } else { pat.always_deref = false; let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; - pat.replacements.push((e.span, format!("&{}", snip))); + pat.replacements.push((e.span, format!("&{snip}"))); } }, _ if !e.span.from_expansion() => { // Double reference might be needed at this point. pat.always_deref = false; let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); - pat.replacements.push((e.span, format!("&{}", snip))); + pat.replacements.push((e.span, format!("&{snip}"))); }, // Edge case for macros. The span of the identifier will usually match the context of the // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 751ca24d5f59..3fac93dcc90c 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -191,7 +191,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.63.0"] pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, - style, + nursery, "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs new file mode 100644 index 000000000000..5ab7144e2909 --- /dev/null +++ b/clippy_lints/src/disallowed_macros.rs @@ -0,0 +1,151 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::macro_backtrace; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Expr, 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_span::{ExpnId, Span}; + +use crate::utils::conf; + +declare_clippy_lint! { + /// ### What it does + /// Denies the configured macros in clippy.toml + /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// macros are defined in the clippy.toml file. + /// + /// ### Why is this bad? + /// Some macros are undesirable in certain contexts, and it's beneficial to + /// lint for them as needed. + /// + /// ### Example + /// An example clippy.toml configuration: + /// ```toml + /// # clippy.toml + /// disallowed-macros = [ + /// # Can use a string as the path of the disallowed macro. + /// "std::print", + /// # Can also use an inline table with a `path` key. + /// { path = "std::println" }, + /// # When using an inline table, can add a `reason` for why the macro + /// # is disallowed. + /// { path = "serde::Serialize", reason = "no serializing" }, + /// ] + /// ``` + /// ``` + /// use serde::Serialize; + /// + /// // Example code where clippy issues a warning + /// println!("warns"); + /// + /// // The diagnostic will contain the message "no serializing" + /// #[derive(Serialize)] + /// struct Data { + /// name: String, + /// value: usize, + /// } + /// ``` + #[clippy::version = "1.65.0"] + pub DISALLOWED_MACROS, + style, + "use of a disallowed macro" +} + +pub struct DisallowedMacros { + conf_disallowed: Vec, + disallowed: DefIdMap, + seen: FxHashSet, +} + +impl DisallowedMacros { + pub fn new(conf_disallowed: Vec) -> Self { + Self { + conf_disallowed, + disallowed: DefIdMap::default(), + seen: FxHashSet::default(), + } + } + + fn check(&mut self, cx: &LateContext<'_>, span: Span) { + if self.conf_disallowed.is_empty() { + return; + } + + for mac in macro_backtrace(span) { + if !self.seen.insert(mac.expn) { + return; + } + + if let Some(&index) = self.disallowed.get(&mac.def_id) { + let conf = &self.conf_disallowed[index]; + + span_lint_and_then( + cx, + DISALLOWED_MACROS, + mac.span, + &format!("use of a disallowed macro `{}`", conf.path()), + |diag| { + if let Some(reason) = conf.reason() { + diag.note(&format!("{reason} (from clippy.toml)")); + } + }, + ); + } + } + } +} + +impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); + +impl LateLintPass<'_> for DisallowedMacros { + fn check_crate(&mut self, cx: &LateContext<'_>) { + for (index, conf) in self.conf_disallowed.iter().enumerate() { + let segs: Vec<_> = conf.path().split("::").collect(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) { + self.disallowed.insert(id, index); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + self.check(cx, expr.span); + } + + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + self.check(cx, stmt.span); + } + + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_>) { + self.check(cx, ty.span); + } + + fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { + self.check(cx, pat.span); + } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + self.check(cx, item.span); + self.check(cx, item.vis_span); + } + + fn check_foreign_item(&mut self, cx: &LateContext<'_>, item: &ForeignItem<'_>) { + self.check(cx, item.span); + self.check(cx, item.vis_span); + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) { + self.check(cx, item.span); + self.check(cx, item.vis_span); + } + + fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { + self.check(cx, item.span); + } + + fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) { + self.check(cx, path.span); + } +} diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 53973ab792a9..1a381f92c031 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; -use rustc_hir::{def::Res, def_id::DefIdMap, Expr, ExprKind}; +use rustc_hir::def::{Namespace, Res}; +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}; @@ -58,12 +60,12 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedMethods { - conf_disallowed: Vec, + conf_disallowed: Vec, disallowed: DefIdMap, } impl DisallowedMethods { - pub fn new(conf_disallowed: Vec) -> Self { + pub fn new(conf_disallowed: Vec) -> Self { Self { conf_disallowed, disallowed: DefIdMap::default(), @@ -77,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) { self.disallowed.insert(id, index); } } @@ -102,11 +104,8 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }; let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { - if let conf::DisallowedMethod::WithReason { - reason: Some(reason), .. - } = conf - { - diag.note(&format!("{} (from clippy.toml)", reason)); + if let Some(reason) = conf.reason() { + diag.note(&format!("{reason} (from clippy.toml)")); } }); } diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs index 0c27c3f9255f..084190f00132 100644 --- a/clippy_lints/src/disallowed_script_idents.rs +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -99,8 +99,7 @@ impl EarlyLintPass for DisallowedScriptIdents { DISALLOWED_SCRIPT_IDENTS, span, &format!( - "identifier `{}` has a Unicode script that is not allowed by configuration: {}", - symbol_str, + "identifier `{symbol_str}` has a Unicode script that is not allowed by configuration: {}", script.full_name() ), ); diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 28dbfbab2e19..c7131fc164d3 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{ - def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind, -}; +use rustc_hir::def::{Namespace, 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_span::Span; @@ -52,13 +52,13 @@ declare_clippy_lint! { } #[derive(Clone, Debug)] pub struct DisallowedTypes { - conf_disallowed: Vec, + conf_disallowed: Vec, def_ids: FxHashMap>, prim_tys: FxHashMap>, } impl DisallowedTypes { - pub fn new(conf_disallowed: Vec) -> Self { + pub fn new(conf_disallowed: Vec) -> Self { Self { conf_disallowed, def_ids: FxHashMap::default(), @@ -88,15 +88,9 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_crate(&mut self, cx: &LateContext<'_>) { for conf in &self.conf_disallowed { - let (path, reason) = match conf { - conf::DisallowedType::Simple(path) => (path, None), - conf::DisallowedType::WithReason { path, reason } => ( - path, - reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)), - ), - }; - let segs: Vec<_> = path.split("::").collect(); - match clippy_utils::def_path_res(cx, &segs) { + let segs: Vec<_> = conf.path().split("::").collect(); + let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)")); + match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { Res::Def(_, id) => { self.def_ids.insert(id, reason); }, @@ -130,7 +124,7 @@ fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) { cx, DISALLOWED_TYPES, span, - &format!("`{}` is not allowed according to config", name), + &format!("`{name}` is not allowed according to config"), |diag| { if let Some(reason) = reason { diag.note(reason); diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index f48ba526d51e..36dc7e3396b8 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -198,6 +198,29 @@ declare_clippy_lint! { "presence of `fn main() {` in code examples" } +declare_clippy_lint! { + /// ### What it does + /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) + /// outside of code blocks + /// ### Why is this bad? + /// It is likely a typo when defining an intra-doc link + /// + /// ### Example + /// ```rust + /// /// See also: ['foo'] + /// fn bar() {} + /// ``` + /// Use instead: + /// ```rust + /// /// See also: [`foo`] + /// fn bar() {} + /// ``` + #[clippy::version = "1.63.0"] + pub DOC_LINK_WITH_QUOTES, + pedantic, + "possible typo for an intra-doc link" +} + #[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { @@ -214,9 +237,14 @@ impl DocMarkdown { } } -impl_lint_pass!(DocMarkdown => - [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN] -); +impl_lint_pass!(DocMarkdown => [ + DOC_LINK_WITH_QUOTES, + DOC_MARKDOWN, + MISSING_SAFETY_DOC, + MISSING_ERRORS_DOC, + MISSING_PANICS_DOC, + NEEDLESS_DOCTEST_MAIN +]); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_crate(&mut self, cx: &LateContext<'tcx>) { @@ -237,7 +265,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers(cx, item.def_id.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); + lint_for_missing_headers( + cx, + item.def_id.def_id, + item.span, + sig, + headers, + Some(body_id), + fpu.panic_span, + ); } }, hir::ItemKind::Impl(impl_) => { @@ -287,7 +323,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers(cx, item.def_id.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); + lint_for_missing_headers( + cx, + item.def_id.def_id, + item.span, + sig, + headers, + Some(body_id), + fpu.panic_span, + ); } } } @@ -416,7 +460,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: (no_stars, sizes) } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] struct DocHeaders { safety: bool, errors: bool, @@ -460,11 +504,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs } if doc.is_empty() { - return DocHeaders { - safety: false, - errors: false, - panics: false, - }; + return DocHeaders::default(); } let mut cb = fake_broken_link_callback; @@ -505,11 +545,7 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range, + in_link: bool, + trimmed_text: &str, + span: Span, + range: &Range, + begin: usize, + text_len: usize, +) { + if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') { + // fix the span to only point at the text within the link + let lo = span.lo() + BytePos::from_usize(range.start - begin); + span_lint( + cx, + DOC_LINK_WITH_QUOTES, + span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)), + "possible intra-doc link using quotes instead of backticks", + ); + } +} + fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) { let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) { Ok(o) => o, @@ -790,7 +848,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { diag.span_suggestion_with_style( span, "try", - format!("`{}`", snippet), + format!("`{snippet}`"), applicability, // always show the suggestion in a separate line, since the // inline presentation adds another pair of backticks diff --git a/clippy_lints/src/doc_link_with_quotes.rs b/clippy_lints/src/doc_link_with_quotes.rs deleted file mode 100644 index 0ff1d2755daf..000000000000 --- a/clippy_lints/src/doc_link_with_quotes.rs +++ /dev/null @@ -1,60 +0,0 @@ -use clippy_utils::diagnostics::span_lint; -use itertools::Itertools; -use rustc_ast::{AttrKind, Attribute}; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) - /// outside of code blocks - /// ### Why is this bad? - /// It is likely a typo when defining an intra-doc link - /// - /// ### Example - /// ```rust - /// /// See also: ['foo'] - /// fn bar() {} - /// ``` - /// Use instead: - /// ```rust - /// /// See also: [`foo`] - /// fn bar() {} - /// ``` - #[clippy::version = "1.63.0"] - pub DOC_LINK_WITH_QUOTES, - pedantic, - "possible typo for an intra-doc link" -} -declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]); - -impl EarlyLintPass for DocLinkWithQuotes { - fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) { - if let AttrKind::DocComment(_, symbol) = attr.kind { - if contains_quote_link(symbol.as_str()) { - span_lint( - ctx, - DOC_LINK_WITH_QUOTES, - attr.span, - "possible intra-doc link using quotes instead of backticks", - ); - } - } - } -} - -fn contains_quote_link(s: &str) -> bool { - let mut in_backticks = false; - let mut found_opening = false; - - for c in s.chars().tuple_windows::<(char, char)>() { - match c { - ('`', _) => in_backticks = !in_backticks, - ('[', '\'') if !in_backticks => found_opening = true, - ('\'', ']') if !in_backticks && found_opening => return true, - _ => {}, - } - } - - false -} diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index b35f0b8ca52d..4721a7b37056 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::get_parent_node; use clippy_utils::is_must_use_func_call; use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; -use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -202,11 +203,13 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id) { let arg_ty = cx.typeck_results().expr_ty(arg); + let is_copy = is_copy(cx, arg_ty); + let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let (lint, msg) = match fn_name { sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY), sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY), - sym::mem_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY), - sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_COPY_SUMMARY), + sym::mem_drop if is_copy && !drop_is_single_call_in_arm => (DROP_COPY, DROP_COPY_SUMMARY), + sym::mem_forget if is_copy => (FORGET_COPY, FORGET_COPY_SUMMARY), sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => { span_lint_and_help( cx, @@ -221,7 +224,9 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { sym::mem_drop if !(arg_ty.needs_drop(cx.tcx, cx.param_env) || is_must_use_func_call(cx, arg) - || is_must_use_ty(cx, arg_ty)) => + || is_must_use_ty(cx, arg_ty) + || drop_is_single_call_in_arm + ) => { (DROP_NON_DROP, DROP_NON_DROP_SUMMARY) }, @@ -236,8 +241,23 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { expr.span, msg, Some(arg.span), - &format!("argument has type `{}`", arg_ty), + &format!("argument has type `{arg_ty}`"), ); } } } + +// dropping returned value of a function like in the following snippet is considered idiomatic, see +// #9482 for examples match { +// => drop(fn_with_side_effect_and_returning_some_value()), +// .. +// } +fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool { + if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) { + let parent_node = get_parent_node(cx.tcx, drop_expr.hir_id); + if let Some(Node::Arm(Arm { body, .. })) = &parent_node { + return body.hir_id == drop_expr.hir_id; + } + } + false +} diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index e70df3f53c75..9c834cf01448 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -113,13 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { ), }; format!( - "if let {}::{} = {}.entry({}) {} else {}", + "if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}", map_ty.entry_path(), - entry_kind, - map_str, - key_str, - then_str, - else_str, ) } else { // if .. { insert } else { insert } @@ -137,16 +132,11 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { let indent_str = snippet_indent(cx, expr.span); let indent_str = indent_str.as_deref().unwrap_or(""); format!( - "match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\ - {indent} {entry}::{} => {}\n{indent}}}", - map_str, - key_str, - then_entry, + "match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\ + {indent_str} {entry}::{else_entry} => {}\n{indent_str}}}", reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())), - else_entry, reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())), entry = map_ty.entry_path(), - indent = indent_str, ) } } else { @@ -163,20 +153,16 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { then_search.snippet_occupied(cx, then_expr.span, &mut app) }; format!( - "if let {}::{} = {}.entry({}) {}", + "if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}", map_ty.entry_path(), - entry_kind, - map_str, - key_str, - body_str, ) } else if let Some(insertion) = then_search.as_single_insertion() { let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0; if contains_expr.negated { if insertion.value.can_have_side_effects() { - format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str) + format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});") } else { - format!("{}.entry({}).or_insert({});", map_str, key_str, value_str) + format!("{map_str}.entry({key_str}).or_insert({value_str});") } } else { // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. @@ -186,7 +172,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { } else { let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app); if contains_expr.negated { - format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str) + format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});") } else { // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. // This would need to be a different lint. diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index c39a909b3ccb..b019d07d53d1 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -202,12 +202,11 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n cx, ENUM_VARIANT_NAMES, span, - &format!("all variants have the same {}fix: `{}`", what, value), + &format!("all variants have the same {what}fix: `{value}`"), None, &format!( - "remove the {}fixes and use full paths to \ - the variants instead of glob imports", - what + "remove the {what}fixes and use full paths to \ + the variants instead of glob imports" ), ); } diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index bce49165e5b1..b40cb7cddaf1 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -51,9 +51,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { 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_some() && array_rec(a) - } + 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), PatKind::Path(_) | PatKind::Lit(_) => true, } @@ -93,9 +91,8 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { "this pattern matching can be expressed using equality", "try", format!( - "{} == {}", + "{} == {pat_str}", snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, - pat_str, ), applicability, ); diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 8ccc969646ec..2e608fe527fd 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -10,7 +11,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::kw; use rustc_target::spec::abi::Abi; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; #[derive(Copy, Clone)] pub struct BoxedLocal { @@ -177,7 +177,13 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { } } - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 598f8c31859e..3732410e71e5 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use if_chain::if_chain; @@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::{self, ClosureKind, Ty, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -122,15 +122,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { then { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if_chain! { - if let ty::Closure(_, substs) = callee_ty.peel_refs().kind(); - if substs.as_closure().kind() == ClosureKind::FnMut; - if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)); - - then { + if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait() + && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &[]) + && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)) + { // Mutable closure is used after current expr; we cannot consume it. - snippet = format!("&mut {}", snippet); - } + snippet = format!("&mut {snippet}"); } diag.span_suggestion( expr.span, @@ -157,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { diag.span_suggestion( expr.span, "replace the closure with the method itself", - format!("{}::{}", name, path.ident.name), + format!("{name}::{}", path.ident.name), Applicability::MachineApplicable, ); }) diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index f3d9ebc5f12d..be6242bd20b8 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -97,7 +97,7 @@ impl LateLintPass<'_> for ExhaustiveItems { item.span, msg, |diag| { - let sugg = format!("#[non_exhaustive]\n{}", indent); + let sugg = format!("#[non_exhaustive]\n{indent}"); diag.span_suggestion(suggestion_span, "try adding #[non_exhaustive]", sugg, diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index b9ed4af02190..c0ea6f338a23 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -80,12 +80,12 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { // used. let (used, sugg_mac) = if let Some(macro_name) = calling_macro { ( - format!("{}!({}(), ...)", macro_name, dest_name), + format!("{macro_name}!({dest_name}(), ...)"), macro_name.replace("write", "print"), ) } else { ( - format!("{}().write_fmt(...)", dest_name), + format!("{dest_name}().write_fmt(...)"), "print".into(), ) }; @@ -100,9 +100,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { cx, EXPLICIT_WRITE, expr.span, - &format!("use of `{}.unwrap()`", used), + &format!("use of `{used}.unwrap()`"), "try this", - format!("{}{}!({})", prefix, sugg_mac, inputs_snippet), + format!("{prefix}{sugg_mac}!({inputs_snippet})"), applicability, ) } diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index f2e079809637..6fee7fb308ce 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -173,9 +173,9 @@ impl FloatFormat { T: fmt::UpperExp + fmt::LowerExp + fmt::Display, { match self { - Self::LowerExp => format!("{:e}", f), - Self::UpperExp => format!("{:E}", f), - Self::Normal => format!("{}", f), + Self::LowerExp => format!("{f:e}"), + Self::UpperExp => format!("{f:E}"), + Self::Normal => format!("{f}"), } } } diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index ba53a9678801..0ed301964758 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -142,8 +142,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node; then { let op = format!( - "{}{}{}", - suggestion, + "{suggestion}{}{}", // Check for float literals without numbers following the decimal // separator such as `2.` and adds a trailing zero if sym.as_str().ends_with('.') { @@ -172,7 +171,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, ar expr.span, "logarithm for bases 2, 10 and e can be computed more accurately", "consider using", - format!("{}.{}()", Sugg::hir(cx, receiver, "..").maybe_par(), method), + format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_par()), Applicability::MachineApplicable, ); } @@ -251,7 +250,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: expr.span, "exponent for bases 2 and e can be computed more accurately", "consider using", - format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method), + format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])), Applicability::MachineApplicable, ); } @@ -312,7 +311,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: if let ExprKind::Binary( Spanned { - node: BinOpKind::Add, .. + node: op @ (BinOpKind::Add | BinOpKind::Sub), + .. }, lhs, rhs, @@ -320,6 +320,16 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: { let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; + // Negate expr if original code has subtraction and expr is on the right side + let maybe_neg_sugg = |expr, hir_id| { + let sugg = Sugg::hir(cx, expr, ".."); + if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { + format!("-{sugg}") + } else { + sugg.to_string() + } + }; + span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, @@ -329,8 +339,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: format!( "{}.mul_add({}, {})", Sugg::hir(cx, receiver, "..").maybe_par(), - Sugg::hir(cx, receiver, ".."), - Sugg::hir(cx, other_addend, ".."), + maybe_neg_sugg(receiver, expr.hir_id), + maybe_neg_sugg(other_addend, other_addend.hir_id), ), Applicability::MachineApplicable, ); @@ -444,7 +454,8 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&' fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Binary( Spanned { - node: BinOpKind::Add, .. + node: op @ (BinOpKind::Add | BinOpKind::Sub), + .. }, lhs, rhs, @@ -458,10 +469,27 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { } } + let maybe_neg_sugg = |expr| { + let sugg = Sugg::hir(cx, expr, ".."); + if let BinOpKind::Sub = op { + format!("-{sugg}") + } else { + sugg.to_string() + } + }; + let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) { - (inner_lhs, inner_rhs, rhs) + ( + inner_lhs, + Sugg::hir(cx, inner_rhs, "..").to_string(), + maybe_neg_sugg(rhs), + ) } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) { - (inner_lhs, inner_rhs, lhs) + ( + inner_lhs, + maybe_neg_sugg(inner_rhs), + Sugg::hir(cx, lhs, "..").to_string(), + ) } else { return; }; @@ -472,12 +500,7 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "multiply and add expressions can be calculated more efficiently and accurately", "consider using", - format!( - "{}.mul_add({}, {})", - prepare_receiver_sugg(cx, recv), - Sugg::hir(cx, arg1, ".."), - Sugg::hir(cx, arg2, ".."), - ), + format!("{}.mul_add({arg1}, {arg2})", prepare_receiver_sugg(cx, recv)), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index f10d82569536..bc0c68f535a9 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { [_] => { // Simulate macro expansion, converting {{ and }} to { and }. let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}"); - let sugg = format!("{}.to_string()", s_expand); + let sugg = format!("{s_expand}.to_string()"); span_useless_format(cx, call_site, sugg, applicability); }, [..] => {}, diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 9e1eaf248b73..cefebc2a98a2 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,16 +1,18 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::is_diag_trait_item; -use clippy_utils::macros::{is_format_macro, FormatArgsExpn}; +use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; +use clippy_utils::macros::{is_format_macro, FormatArgsExpn, FormatParam, FormatParamUsage}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; +use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; use if_chain::if_chain; use itertools::Itertools; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol}; declare_clippy_lint! { @@ -64,7 +66,67 @@ declare_clippy_lint! { "`to_string` applied to a type that implements `Display` in format args" } -declare_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); +declare_clippy_lint! { + /// ### What it does + /// Detect when a variable is not inlined in a format string, + /// and suggests to inline it. + /// + /// ### Why is this bad? + /// Non-inlined code is slightly more difficult to read and understand, + /// as it requires arguments to be matched against the format string. + /// The inlined syntax, where allowed, is simpler. + /// + /// ### Example + /// ```rust + /// # let var = 42; + /// # let width = 1; + /// # let prec = 2; + /// format!("{}", var); + /// format!("{v:?}", v = var); + /// format!("{0} {0}", var); + /// format!("{0:1$}", var, width); + /// format!("{:.*}", prec, var); + /// ``` + /// Use instead: + /// ```rust + /// # let var = 42; + /// # let width = 1; + /// # let prec = 2; + /// format!("{var}"); + /// format!("{var:?}"); + /// format!("{var} {var}"); + /// format!("{var:width$}"); + /// format!("{var:.prec$}"); + /// ``` + /// + /// ### Known Problems + /// + /// There may be a false positive if the format string is expanded from certain proc macros: + /// + /// ```ignore + /// println!(indoc!("{}"), var); + /// ``` + /// + /// If a format string contains a numbered argument that cannot be inlined + /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. + #[clippy::version = "1.65.0"] + pub UNINLINED_FORMAT_ARGS, + pedantic, + "using non-inlined variables in `format!` calls" +} + +impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); + +pub struct FormatArgs { + msrv: Option, +} + +impl FormatArgs { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -86,9 +148,72 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); check_to_string_in_format_args(cx, name, arg.param.value); } + if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site); + } } } } + + extract_msrv_attr!(LateContext); +} + +fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span) { + if args.format_string.span.from_expansion() { + return; + } + + let mut fixes = Vec::new(); + // If any of the arguments are referenced by an index number, + // and that argument is not a simple variable and cannot be inlined, + // we cannot remove any other arguments in the format string, + // because the index numbers might be wrong after inlining. + // Example of an un-inlinable format: print!("{}{1}", foo, 2) + if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() { + return; + } + + // FIXME: Properly ignore a rare case where the format string is wrapped in a macro. + // Example: `format!(indoc!("{}"), foo);` + // If inlined, they will cause a compilation error: + // > to avoid ambiguity, `format_args!` cannot capture variables + // > when the format string is expanded from a macro + // @Alexendoo explanation: + // > indoc! is a proc macro that is producing a string literal with its span + // > set to its input it's not marked as from expansion, and since it's compatible + // > tokenization wise clippy_utils::is_from_proc_macro wouldn't catch it either + // This might be a relatively expensive test, so do it only we are ready to replace. + // See more examples in tests/ui/uninlined_format_args.rs + + span_lint_and_then( + cx, + UNINLINED_FORMAT_ARGS, + call_site, + "variables can be used directly in the `format!` string", + |diag| { + diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable); + }, + ); +} + +fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool { + if matches!(param.kind, Implicit | Starred | Named(_) | Numbered) + && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind + && let [segment] = path.segments + && let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id) + { + let replacement = match param.usage { + FormatParamUsage::Argument => segment.ident.name.to_string(), + FormatParamUsage::Width => format!("{}$", segment.ident.name), + FormatParamUsage::Precision => format!(".{}$", segment.ident.name), + }; + fixes.push((param.span, replacement)); + fixes.push((arg_span, String::new())); + true // successful inlining, continue checking + } else { + // if we can't inline a numbered argument, we can't continue + param.kind != Numbered + } } fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { @@ -117,11 +242,10 @@ fn check_format_in_format_args( cx, FORMAT_IN_FORMAT_ARGS, call_site, - &format!("`format!` in `{}!` args", name), + &format!("`format!` in `{name}!` args"), |diag| { diag.help(&format!( - "combine the `format!(..)` arguments with the outer `{}!(..)` call", - name + "combine the `format!(..)` arguments with the outer `{name}!(..)` call" )); diag.help("or consider changing `format!` to `format_args!`"); }, @@ -149,8 +273,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex TO_STRING_IN_FORMAT_ARGS, value.span.with_lo(receiver.span.hi()), &format!( - "`to_string` applied to a type that implements `Display` in `{}!` args", - name + "`to_string` applied to a type that implements `Display` in `{name}!` args" ), "remove this", String::new(), @@ -162,16 +285,13 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex TO_STRING_IN_FORMAT_ARGS, value.span, &format!( - "`to_string` applied to a type that implements `Display` in `{}!` args", - name + "`to_string` applied to a type that implements `Display` in `{name}!` args" ), "use this", format!( - "{}{:*>width$}{}", + "{}{:*>n_needed_derefs$}{receiver_snippet}", if needs_ref { "&" } else { "" }, - "", - receiver_snippet, - width = n_needed_derefs + "" ), Applicability::MachineApplicable, ); @@ -180,7 +300,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex } } -// Returns true if `hir_id` is referred to by multiple format params +/// Returns true if `hir_id` is referred to by multiple format params fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool { args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err() } diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index b628fd9f7581..ed1342a54654 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -214,12 +214,12 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: cx, PRINT_IN_FORMAT_IMPL, macro_call.span, - &format!("use of `{}!` in `{}` impl", name, impl_trait.name), + &format!("use of `{name}!` in `{}` impl", impl_trait.name), "replace with", if let Some(formatter_name) = impl_trait.formatter_name { - format!("{}!({}, ..)", replacement, formatter_name) + format!("{replacement}!({formatter_name}, ..)") } else { - format!("{}!(..)", replacement) + format!("{replacement}!(..)") }, Applicability::HasPlaceholders, ); diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 01cefe4af853..a866a68987d0 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -154,11 +154,10 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { eqop_span, &format!( "this looks like you are trying to use `.. {op}= ..`, but you \ - really are doing `.. = ({op} ..)`", - op = op + really are doing `.. = ({op} ..)`" ), None, - &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op), + &format!("to remove this lint, use either `{op}=` or `= {op}`"), ); } } @@ -191,16 +190,12 @@ fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { SUSPICIOUS_UNARY_OP_FORMATTING, eqop_span, &format!( - "by not having a space between `{binop}` and `{unop}` it looks like \ - `{binop}{unop}` is a single operator", - binop = binop_str, - unop = unop_str + "by not having a space between `{binop_str}` and `{unop_str}` it looks like \ + `{binop_str}{unop_str}` is a single operator" ), None, &format!( - "put a space between `{binop}` and `{unop}` and remove the space after `{unop}`", - binop = binop_str, - unop = unop_str + "put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`" ), ); } @@ -246,12 +241,11 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { cx, SUSPICIOUS_ELSE_FORMATTING, else_span, - &format!("this is an `else {}` but the formatting might hide it", else_desc), + &format!("this is an `else {else_desc}` but the formatting might hide it"), None, &format!( "to remove this lint, remove the `else` or remove the new line between \ - `else` and `{}`", - else_desc, + `else` and `{else_desc}`", ), ); } @@ -320,11 +314,10 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { cx, SUSPICIOUS_ELSE_FORMATTING, else_span, - &format!("this looks like {} but the `else` is missing", looks_like), + &format!("this looks like {looks_like} but the `else` is missing"), None, &format!( - "to remove this lint, add the missing `else` or add a new line before {}", - next_thing, + "to remove this lint, add the missing `else` or add a new line before {next_thing}", ), ); } diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 74941d817be3..cf8b7acd66d2 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; @@ -60,8 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { if pathseg.ident.name.as_str() == "from_str_radix"; // check if the second argument is a primitive `10` - if let ExprKind::Lit(lit) = &radix.kind; - if let rustc_ast::ast::LitKind::Int(10, _) = lit.node; + if is_integer_literal(radix, 10); then { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { exp.span, "this call to `from_str_radix` can be replaced with a call to `str::parse`", "try", - format!("{}.parse::<{}>()", sugg, prim_ty.name_str()), + format!("{sugg}.parse::<{}>()", prim_ty.name_str()), Applicability::MaybeIncorrect ); } diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index d6d33bda1738..d263804f32cf 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -1,7 +1,7 @@ use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::def_id::{DefIdSet, LocalDefId}; -use rustc_hir::{self as hir, def::Res, intravisit, QPath}; +use rustc_hir::{self as hir, def::Res, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::{ lint::in_external_macro, @@ -13,8 +13,11 @@ use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; +use core::ops::ControlFlow; + use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { @@ -47,7 +50,8 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id.def_id).is_none() { + } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id.def_id).is_none() + { check_must_use_candidate( cx, sig.decl, @@ -143,7 +147,7 @@ fn check_must_use_candidate<'tcx>( diag.span_suggestion( fn_span, "add the attribute", - format!("#[must_use] {}", snippet), + format!("#[must_use] {snippet}"), Applicability::MachineApplicable, ); } @@ -199,79 +203,65 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m } } -struct StaticMutVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - mutates_static: bool, +fn is_mutated_static(e: &hir::Expr<'_>) -> bool { + use hir::ExprKind::{Field, Index, Path}; + + match e.kind { + Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), + Path(_) => true, + Field(inner, _) | Index(inner, _) => is_mutated_static(inner), + _ => false, + } } -impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { +fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { + for_each_expr(body.value, |e| { use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall}; - if self.mutates_static { - return; - } - match expr.kind { + match e.kind { Call(_, args) => { let mut tys = DefIdSet::default(); for arg in args { - if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) + if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) && is_mutable_ty( - self.cx, - self.cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), + cx, + cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), arg.span, &mut tys, ) && is_mutated_static(arg) { - self.mutates_static = true; - return; + return ControlFlow::Break(()); } tys.clear(); } + ControlFlow::Continue(()) }, MethodCall(_, receiver, args, _) => { let mut tys = DefIdSet::default(); for arg in std::iter::once(receiver).chain(args.iter()) { - if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) + if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) && is_mutable_ty( - self.cx, - self.cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), + cx, + cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), arg.span, &mut tys, ) && is_mutated_static(arg) { - self.mutates_static = true; - return; + return ControlFlow::Break(()); } tys.clear(); } + ControlFlow::Continue(()) }, - Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => { - self.mutates_static |= is_mutated_static(target); + Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) + if is_mutated_static(target) => + { + ControlFlow::Break(()) }, - _ => {}, + _ => ControlFlow::Continue(()), } - } -} - -fn is_mutated_static(e: &hir::Expr<'_>) -> bool { - use hir::ExprKind::{Field, Index, Path}; - - match e.kind { - Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), - Path(_) => true, - Field(inner, _) | Index(inner, _) => is_mutated_static(inner), - _ => false, - } -} - -fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool { - let mut v = StaticMutVisitor { - cx, - mutates_static: false, - }; - intravisit::walk_expr(&mut v, body.value); - v.mutates_static + }) + .is_some() } diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 0b50431fbaab..b7595d101e0f 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -5,8 +5,11 @@ use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::type_is_unsafe_function; +use clippy_utils::visitors::for_each_expr_with_closures; use clippy_utils::{iter_input_pats, path_to_local}; +use core::ops::ControlFlow; + use super::NOT_UNSAFE_PTR_ARG_DEREF; pub(super) fn check_fn<'tcx>( @@ -39,21 +42,34 @@ fn check_raw_ptr<'tcx>( body: &'tcx hir::Body<'tcx>, def_id: LocalDefId, ) { - let expr = &body.value; if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) { let raw_ptrs = iter_input_pats(decl, body) .filter_map(|arg| raw_ptr_arg(cx, arg)) .collect::(); if !raw_ptrs.is_empty() { - let typeck_results = cx.tcx.typeck_body(body.id()); - let mut v = DerefVisitor { - cx, - ptrs: raw_ptrs, - typeck_results, - }; - - intravisit::walk_expr(&mut v, expr); + let typeck = cx.tcx.typeck_body(body.id()); + let _: Option = for_each_expr_with_closures(cx, body.value, |e| { + match e.kind { + hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => { + for arg in args { + check_arg(cx, &raw_ptrs, arg); + } + }, + hir::ExprKind::MethodCall(_, recv, args, _) => { + let def_id = typeck.type_dependent_def_id(e.hir_id).unwrap(); + if cx.tcx.fn_sig(def_id).skip_binder().unsafety == hir::Unsafety::Unsafe { + check_arg(cx, &raw_ptrs, recv); + for arg in args { + check_arg(cx, &raw_ptrs, arg); + } + } + }, + hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => check_arg(cx, &raw_ptrs, ptr), + _ => (), + } + ControlFlow::Continue(()) + }); } } } @@ -70,54 +86,13 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option } } -struct DerefVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - ptrs: HirIdSet, - typeck_results: &'a ty::TypeckResults<'tcx>, -} - -impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - match expr.kind { - hir::ExprKind::Call(f, args) => { - let ty = self.typeck_results.expr_ty(f); - - if type_is_unsafe_function(self.cx, ty) { - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::MethodCall(_, receiver, args, _) => { - let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap(); - let base_type = self.cx.tcx.type_of(def_id); - - if type_is_unsafe_function(self.cx, base_type) { - self.check_arg(receiver); - for arg in args { - self.check_arg(arg); - } - } - }, - hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => self.check_arg(ptr), - _ => (), - } - - intravisit::walk_expr(self, expr); - } -} - -impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { - fn check_arg(&self, ptr: &hir::Expr<'_>) { - if let Some(id) = path_to_local(ptr) { - if self.ptrs.contains(&id) { - span_lint( - self.cx, - NOT_UNSAFE_PTR_ARG_DEREF, - ptr.span, - "this public function might dereference a raw pointer but is not marked `unsafe`", - ); - } - } +fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) { + if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) { + span_lint( + cx, + NOT_UNSAFE_PTR_ARG_DEREF, + arg.span, + "this public function might dereference a raw pointer but is not marked `unsafe`", + ); } } diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 5c8d8b8e7552..1e08922a6166 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -59,10 +59,7 @@ fn check_arg_number(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, fn_span: Span, cx, TOO_MANY_ARGUMENTS, fn_span, - &format!( - "this function has too many arguments ({}/{})", - args, too_many_arguments_threshold - ), + &format!("this function has too many arguments ({args}/{too_many_arguments_threshold})"), ); } } diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index 54bdea7ea25d..f83f8b40f94b 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -78,10 +78,7 @@ pub(super) fn check_fn( cx, TOO_MANY_LINES, span, - &format!( - "this function has too many lines ({}/{})", - line_count, too_many_lines_threshold - ), + &format!("this function has too many lines ({line_count}/{too_many_lines_threshold})"), ); } } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 11c43247868c..0d6718c168a5 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks}; +use clippy_utils::{ + contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks, +}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -76,15 +78,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind - && let ExprKind::Path(ref then_call_qpath) = then_call.kind - && is_lang_ctor(cx, then_call_qpath, OptionSome) - && let ExprKind::Path(ref qpath) = peel_blocks(els).kind - && is_lang_ctor(cx, qpath, OptionNone) + && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) + && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !stmts_contains_early_return(then_block.stmts) { let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { - format!("({})", cond_snip) + format!("({cond_snip})") } else { cond_snip.into_owned() }; @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { let mut method_body = if then_block.stmts.is_empty() { arg_snip.into_owned() } else { - format!("{{ /* snippet */ {} }}", arg_snip) + format!("{{ /* snippet */ {arg_snip} }}") }; let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { "then_some" @@ -102,14 +102,13 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { }; let help = format!( - "consider using `bool::{}` like: `{}.{}({})`", - method_name, cond_snip, method_name, method_body, + "consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`", ); span_lint_and_help( cx, IF_THEN_SOME_ELSE_NONE, expr.span, - &format!("this could be simplified with `bool::{}`", method_name), + &format!("this could be simplified with `bool::{method_name}`"), None, &help, ); diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index a920c3bba2ae..93efe957b1dc 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -5,6 +5,7 @@ use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; @@ -12,7 +13,6 @@ use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; -use rustc_hir_analysis::hir_ty_to_ty; use if_chain::if_chain; @@ -89,8 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { ( generics_suggestion_span, format!( - "<{}{}S: ::std::hash::BuildHasher{}>", - generics_snip, + "<{generics_snip}{}S: ::std::hash::BuildHasher{}>", if generics_snip.is_empty() { "" } else { ", " }, if vis.suggestions.is_empty() { "" @@ -263,8 +262,8 @@ impl<'tcx> ImplicitHasherType<'tcx> { fn type_arguments(&self) -> String { match *self { - ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v), - ImplicitHasherType::HashSet(.., ref t) => format!("{}", t), + ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{k}, {v}"), + ImplicitHasherType::HashSet(.., ref t) => format!("{t}"), } } diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index feec8ec2e23f..946d04eff6f9 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -2,10 +2,11 @@ use clippy_utils::{ diagnostics::span_lint_hir_and_then, get_async_fn_body, is_async_fn, source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, - visitors::expr_visitor_no_bodies, + visitors::for_each_expr, }; +use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::intravisit::{FnKind, Visitor}; +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; @@ -53,7 +54,7 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) { span, "missing `return` statement", |diag| { - diag.span_suggestion(span, "add `return` as shown", format!("return {}", snip), app); + diag.span_suggestion(span, "add `return` as shown", format!("return {snip}"), app); }, ); } @@ -71,7 +72,7 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp diag.span_suggestion( break_span, "change `break` to `return` as shown", - format!("return {}", snip), + format!("return {snip}"), app, ); }, @@ -152,7 +153,7 @@ fn lint_implicit_returns( ExprKind::Loop(block, ..) => { let mut add_return = false; - expr_visitor_no_bodies(|e| { + let _: Option = for_each_expr(block, |e| { if let ExprKind::Break(dest, sub_expr) = e.kind { if dest.target_id.ok() == Some(expr.hir_id) { if call_site_span.is_none() && e.span.ctxt() == ctxt { @@ -167,9 +168,8 @@ fn lint_implicit_returns( } } } - true - }) - .visit_block(block); + ControlFlow::Continue(()) + }); if add_return { #[expect(clippy::option_if_let_else)] if let Some(span) = call_site_span { diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs new file mode 100644 index 000000000000..bf1351829c6a --- /dev/null +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -0,0 +1,114 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::get_parent_expr; +use clippy_utils::source::snippet_with_applicability; +use if_chain::if_chain; +use rustc_ast::ast::{LitIntType, LitKind}; +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}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for implicit saturating addition. + /// + /// ### Why is this bad? + /// The built-in function is more readable and may be faster. + /// + /// ### Example + /// ```rust + ///let mut u:u32 = 7000; + /// + /// if u != u32::MAX { + /// u += 1; + /// } + /// ``` + /// Use instead: + /// ```rust + ///let mut u:u32 = 7000; + /// + /// u = u.saturating_add(1); + /// ``` + #[clippy::version = "1.65.0"] + pub IMPLICIT_SATURATING_ADD, + style, + "Perform saturating addition instead of implicitly checking max bound of data type" +} +declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]); + +impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let ExprKind::If(cond, then, None) = expr.kind; + if let ExprKind::DropTemps(expr1) = cond.kind; + if let Some((c, op_node, l)) = get_const(cx, expr1); + if let BinOpKind::Ne | BinOpKind::Lt = op_node; + if let ExprKind::Block(block, None) = then.kind; + if let Block { + stmts: + [Stmt + { kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), .. }], + expr: None, ..} | + Block { stmts: [], expr: Some(ex), ..} = block; + if let ExprKind::AssignOp(op1, target, value) = ex.kind; + let ty = cx.typeck_results().expr_ty(target); + if Some(c) == get_int_max(ty); + if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target); + if BinOpKind::Add == op1.node; + if let ExprKind::Lit(ref lit) = value.kind; + if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; + if block.expr.is_none(); + then { + let mut app = Applicability::MachineApplicable; + let code = snippet_with_applicability(cx, target.span, "_", &mut app); + let sugg = if let Some(parent) = get_parent_expr(cx, expr) && let ExprKind::If(_cond, _then, Some(else_)) = parent.kind && else_.hir_id == expr.hir_id {format!("{{{code} = {code}.saturating_add(1); }}")} else {format!("{code} = {code}.saturating_add(1);")}; + span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app); + } + } + } +} + +fn get_int_max(ty: Ty<'_>) -> Option { + match ty.peel_refs().kind() { + Int(IntTy::I8) => i8::max_value().try_into().ok(), + Int(IntTy::I16) => i16::max_value().try_into().ok(), + Int(IntTy::I32) => i32::max_value().try_into().ok(), + Int(IntTy::I64) => i64::max_value().try_into().ok(), + Int(IntTy::I128) => i128::max_value().try_into().ok(), + Int(IntTy::Isize) => isize::max_value().try_into().ok(), + Uint(UintTy::U8) => u8::max_value().try_into().ok(), + Uint(UintTy::U16) => u16::max_value().try_into().ok(), + Uint(UintTy::U32) => u32::max_value().try_into().ok(), + Uint(UintTy::U64) => u64::max_value().try_into().ok(), + Uint(UintTy::U128) => Some(u128::max_value()), + Uint(UintTy::Usize) => usize::max_value().try_into().ok(), + _ => None, + } +} + +fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { + if let ExprKind::Binary(op, l, r) = expr.kind { + let tr = cx.typeck_results(); + if let Some((Constant::Int(c), _)) = constant(cx, tr, r) { + return Some((c, op.node, l)); + }; + if let Some((Constant::Int(c), _)) = constant(cx, tr, l) { + return Some((c, invert_op(op.node)?, r)); + } + } + None +} + +fn invert_op(op: BinOpKind) -> Option { + use rustc_hir::BinOpKind::{Ge, Gt, Le, Lt, Ne}; + match op { + Lt => Some(Gt), + Le => Some(Ge), + Ne => Some(Ne), + Ge => Some(Le), + Gt => Some(Lt), + _ => None, + } +} diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 46654bc61e0f..48edbf6ae576 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{higher, peel_blocks_with_stmt, SpanlessEq}; +use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -131,17 +131,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { match peel_blocks_with_stmt(expr).kind { ExprKind::AssignOp(ref op1, target, value) => { - if_chain! { - if BinOpKind::Sub == op1.node; - // Check if literal being subtracted is one - if let ExprKind::Lit(ref lit1) = value.kind; - if let LitKind::Int(1, _) = lit1.node; - then { - Some(target) - } else { - None - } - } + // Check if literal being subtracted is one + (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target) }, ExprKind::Assign(target, value, _) => { if_chain! { @@ -150,8 +141,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp if SpanlessEq::new(cx).eq_expr(left1, target); - if let ExprKind::Lit(ref lit1) = right1.kind; - if let LitKind::Int(1, _) = lit1.node; + if is_integer_literal(right1, 1); then { Some(target) } else { @@ -170,7 +160,7 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) { expr.span, "implicitly performing saturating subtraction", "try", - format!("{} = {}.saturating_sub({});", var_name, var_name, '1'), + format!("{var_name} = {var_name}.saturating_sub({});", '1'), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 14b22d2b50d0..e2f2d3d42e69 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let mut fields_snippet = String::new(); let (last_ident, idents) = ordered_fields.split_last().unwrap(); for ident in idents { - let _ = write!(fields_snippet, "{}, ", ident); + let _ = write!(fields_snippet, "{ident}, "); } fields_snippet.push_str(&last_ident.to_string()); @@ -100,10 +100,8 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { String::new() }; - let sugg = format!("{} {{ {}{} }}", + let sugg = format!("{} {{ {fields_snippet}{base_snippet} }}", snippet(cx, qpath.span(), ".."), - fields_snippet, - base_snippet, ); span_lint_and_sugg( diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 0dd7f5bf000d..c7b5badaae51 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -139,14 +139,14 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) { .map(|(index, _)| *index) .collect::>(); - let value_name = |index| format!("{}_{}", slice.ident.name, index); + let value_name = |index| format!("{}_{index}", slice.ident.name); if let Some(max_index) = used_indices.iter().max() { let opt_ref = if slice.needs_ref { "ref " } else { "" }; let pat_sugg_idents = (0..=*max_index) .map(|index| { if used_indices.contains(&index) { - format!("{}{}", opt_ref, value_name(index)) + format!("{opt_ref}{}", value_name(index)) } else { "_".to_string() } diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 8c2c96fa105a..d1d2db27c6fc 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::higher; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{higher, match_def_path, path_def_id, paths}; use rustc_hir::{BorrowKind, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -168,9 +168,16 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { }, ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), - ExprKind::Call(path, _) => path_def_id(cx, path) - .map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT)) - .into(), + ExprKind::Call(path, _) => { + if let ExprKind::Path(ref qpath) = path.kind { + cx.qpath_res(qpath, path.hir_id) + .opt_def_id() + .map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id)) + .into() + } else { + Finite + } + }, ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(), _ => Finite, } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index cb6c2ec0fb98..676136df572b 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method}; +use clippy_utils::{return_ty, trait_ref_of_method}; use if_chain::if_chain; use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -118,7 +118,10 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { } fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { - let display_trait_id = get_trait_def_id(cx, &paths::DISPLAY_TRAIT).expect("Failed to get trait ID of `Display`!"); + let display_trait_id = cx + .tcx + .get_diagnostic_item(sym::Display) + .expect("Failed to get trait ID of `Display`!"); // Get the real type of 'self' let self_type = cx.tcx.fn_sig(item.def_id).input(0); @@ -131,23 +134,19 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { INHERENT_TO_STRING_SHADOW_DISPLAY, item.span, &format!( - "type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`", - self_type + "type `{self_type}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`" ), None, - &format!("remove the inherent method from type `{}`", self_type), + &format!("remove the inherent method from type `{self_type}`"), ); } else { span_lint_and_help( cx, INHERENT_TO_STRING, item.span, - &format!( - "implementation of inherent method `to_string(&self) -> String` for type `{}`", - self_type - ), + &format!("implementation of inherent method `to_string(&self) -> String` for type `{self_type}`"), None, - &format!("implement trait `Display` for type `{}` instead", self_type), + &format!("implement trait `Display` for type `{self_type}` instead"), ); } } diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs index dd7177e0131c..d609a5ca4d46 100644 --- a/clippy_lints/src/inline_fn_without_body.rs +++ b/clippy_lints/src/inline_fn_without_body.rs @@ -51,7 +51,7 @@ fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) { cx, INLINE_FN_WITHOUT_BODY, attr.span, - &format!("use of `#[inline]` on trait method `{}` which has no body", name), + &format!("use of `#[inline]` on trait method `{name}` which has no body"), |diag| { diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable); }, diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 9a944def3eb2..33491da3fc5a 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -138,8 +138,8 @@ impl IntPlusOne { if let Some(snippet) = snippet_opt(cx, node.span) { if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) { let rec = match side { - Side::Lhs => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)), - Side::Rhs => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)), + Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), + Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")), }; return rec; } diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 2027c23d328c..ea9f046fb973 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -80,10 +80,7 @@ fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefI cx, ITER_NOT_RETURNING_ITERATOR, sig.span, - &format!( - "this method is named `{}` but its return type does not implement `Iterator`", - name - ), + &format!("this method is named `{name}` but its return type does not implement `Iterator`"), ); } } diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index d6eb53ae29b5..76c83ab47d09 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{BytePos, Pos, Span}; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 7d15dd4cb216..3a563736fb07 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -210,7 +210,8 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } } - if cx.access_levels.is_exported(visited_trait.def_id.def_id) && trait_items.iter().any(|i| is_named_self(cx, i, sym::len)) + if cx.access_levels.is_exported(visited_trait.def_id.def_id) + && trait_items.iter().any(|i| is_named_self(cx, i, sym::len)) { let mut current_and_super_traits = DefIdSet::default(); fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx); @@ -278,15 +279,13 @@ impl<'tcx> LenOutput<'tcx> { _ => "", }; match self { - Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref), - Self::Option(_) => format!( - "expected signature: `({}self) -> bool` or `({}self) -> Option", - self_ref, self_ref - ), - Self::Result(..) => format!( - "expected signature: `({}self) -> bool` or `({}self) -> Result", - self_ref, self_ref - ), + Self::Integral => format!("expected signature: `({self_ref}self) -> bool`"), + Self::Option(_) => { + format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option") + }, + Self::Result(..) => { + format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result") + }, } } } @@ -326,8 +325,7 @@ fn check_for_is_empty<'tcx>( let (msg, is_empty_span, self_kind) = match is_empty { None => ( format!( - "{} `{}` has a public `len` method, but no `is_empty` method", - item_kind, + "{item_kind} `{}` has a public `len` method, but no `is_empty` method", item_name.as_str(), ), None, @@ -335,8 +333,7 @@ fn check_for_is_empty<'tcx>( ), Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => ( format!( - "{} `{}` has a public `len` method, but a private `is_empty` method", - item_kind, + "{item_kind} `{}` has a public `len` method, but a private `is_empty` method", item_name.as_str(), ), Some(cx.tcx.def_span(is_empty.def_id)), @@ -348,8 +345,7 @@ fn check_for_is_empty<'tcx>( { ( format!( - "{} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature", - item_kind, + "{item_kind} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature", item_name.as_str(), ), Some(cx.tcx.def_span(is_empty.def_id)), @@ -419,10 +415,9 @@ fn check_len( LEN_ZERO, span, &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), - &format!("using `{}is_empty` is clearer and more explicit", op), + &format!("using `{op}is_empty` is clearer and more explicit"), format!( - "{}{}.is_empty()", - op, + "{op}{}.is_empty()", snippet_with_applicability(cx, receiver.span, "_", &mut applicability) ), applicability, @@ -439,10 +434,9 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex COMPARISON_TO_EMPTY, span, "comparison to empty slice", - &format!("using `{}is_empty` is clearer and more explicit", op), + &format!("using `{op}is_empty` is clearer and more explicit"), format!( - "{}{}.is_empty()", - op, + "{op}{}.is_empty()", snippet_with_applicability(cx, lit1.span, "_", &mut applicability) ), applicability, diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 10fc0f4018ef..13071d64441a 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -106,8 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { // use mutably after the `if` let sug = format!( - "let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", - mut=mutability, + "let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", name=ident.name, cond=snippet(cx, cond.span, "_"), then=if then.stmts.len() > 1 { " ..;" } else { "" }, diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 8718d5fa1da0..5d26e4b33601 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -21,6 +21,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), LintId::of(borrow_deref_ref::BORROW_DEREF_REF), + LintId::of(box_default::BOX_DEFAULT), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), @@ -44,7 +45,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), + LintId::of(disallowed_macros::DISALLOWED_MACROS), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_names::DISALLOWED_NAMES), LintId::of(disallowed_types::DISALLOWED_TYPES), @@ -85,6 +86,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(if_let_mutex::IF_LET_MUTEX), + LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(infinite_iter::INFINITE_ITER), LintId::of(inherent_to_string::INHERENT_TO_STRING), @@ -125,6 +127,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_bits::MANUAL_BITS), + LintId::of(manual_clamp::MANUAL_CLAMP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), LintId::of(manual_retain::MANUAL_RETAIN), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 185189a6af5b..a58d066fa6b6 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -22,6 +22,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(loops::MANUAL_FLATTEN), LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::WHILE_LET_LOOP), + LintId::of(manual_clamp::MANUAL_CLAMP), LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), LintId::of(manual_strip::MANUAL_STRIP), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs index be63646a12f5..71dfdab369b9 100644 --- a/clippy_lints/src/lib.register_internal.rs +++ b/clippy_lints/src/lib.register_internal.rs @@ -13,10 +13,10 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE), LintId::of(utils::internal_lints::INVALID_PATHS), LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE), LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL), LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), LintId::of(utils::internal_lints::PRODUCE_ICE), + LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH), LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), ]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 02fcc8de5072..05d927dbea79 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -24,8 +24,6 @@ store.register_lints(&[ #[cfg(feature = "internal")] utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal")] - utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - #[cfg(feature = "internal")] utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] utils::internal_lints::MISSING_MSRV_ATTR_IMPL, @@ -34,6 +32,8 @@ store.register_lints(&[ #[cfg(feature = "internal")] utils::internal_lints::PRODUCE_ICE, #[cfg(feature = "internal")] + utils::internal_lints::UNNECESSARY_DEF_PATH, + #[cfg(feature = "internal")] utils::internal_lints::UNNECESSARY_SYMBOL_STR, almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, @@ -60,6 +60,7 @@ store.register_lints(&[ booleans::NONMINIMAL_BOOL, booleans::OVERLY_COMPLEX_BOOL_EXPR, borrow_deref_ref::BORROW_DEREF_REF, + box_default::BOX_DEFAULT, cargo::CARGO_COMMON_METADATA, cargo::MULTIPLE_CRATE_VERSIONS, cargo::NEGATIVE_FEATURE_NAMES, @@ -113,16 +114,17 @@ store.register_lints(&[ derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ, derive::EXPL_IMPL_CLONE_ON_COPY, derive::UNSAFE_DERIVE_DESERIALIZE, + disallowed_macros::DISALLOWED_MACROS, disallowed_methods::DISALLOWED_METHODS, disallowed_names::DISALLOWED_NAMES, disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS, disallowed_types::DISALLOWED_TYPES, + doc::DOC_LINK_WITH_QUOTES, doc::DOC_MARKDOWN, doc::MISSING_ERRORS_DOC, doc::MISSING_PANICS_DOC, doc::MISSING_SAFETY_DOC, doc::NEEDLESS_DOCTEST_MAIN, - doc_link_with_quotes::DOC_LINK_WITH_QUOTES, double_parens::DOUBLE_PARENS, drop_forget_ref::DROP_COPY, drop_forget_ref::DROP_NON_DROP, @@ -159,6 +161,7 @@ store.register_lints(&[ format::USELESS_FORMAT, format_args::FORMAT_IN_FORMAT_ARGS, format_args::TO_STRING_IN_FORMAT_ARGS, + format_args::UNINLINED_FORMAT_ARGS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, format_push_string::FORMAT_PUSH_STRING, @@ -182,6 +185,7 @@ store.register_lints(&[ if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, implicit_hasher::IMPLICIT_HASHER, implicit_return::IMPLICIT_RETURN, + implicit_saturating_add::IMPLICIT_SATURATING_ADD, implicit_saturating_sub::IMPLICIT_SATURATING_SUB, inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, index_refutable_slice::INDEX_REFUTABLE_SLICE, @@ -243,6 +247,7 @@ store.register_lints(&[ manual_assert::MANUAL_ASSERT, manual_async_fn::MANUAL_ASYNC_FN, manual_bits::MANUAL_BITS, + manual_clamp::MANUAL_CLAMP, manual_instant_elapsed::MANUAL_INSTANT_ELAPSED, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_rem_euclid::MANUAL_REM_EUCLID, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index f1783dd9ddef..e0b4639af53e 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -6,6 +6,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), + LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), LintId::of(equatable_if_let::EQUATABLE_IF_LET), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 584ccf55e511..bc2f0beb358a 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -20,15 +20,16 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(dereference::REF_BINDING_TO_REFERENCE), LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), + LintId::of(doc::DOC_LINK_WITH_QUOTES), LintId::of(doc::DOC_MARKDOWN), LintId::of(doc::MISSING_ERRORS_DOC), LintId::of(doc::MISSING_PANICS_DOC), - LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES), LintId::of(empty_enum::EMPTY_ENUM), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(functions::MUST_USE_CANDIDATE), LintId::of(functions::TOO_MANY_LINES), LintId::of(if_not_else::IF_NOT_ELSE), diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index 195ce41e31e9..8e927470e02f 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -3,6 +3,7 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ + LintId::of(box_default::BOX_DEFAULT), LintId::of(entry::MAP_ENTRY), LintId::of(escape::BOXED_LOCAL), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 05d2ec2e9e1e..8e1390167dc8 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -15,7 +15,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY), LintId::of(dereference::NEEDLESS_BORROW), - LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), + LintId::of(disallowed_macros::DISALLOWED_MACROS), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_names::DISALLOWED_NAMES), LintId::of(disallowed_types::DISALLOWED_TYPES), @@ -30,6 +30,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), + LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c3db194c4ad8..2dcefd78763b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -31,6 +31,7 @@ extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; +extern crate rustc_hir_analysis; extern crate rustc_hir_pretty; extern crate rustc_index; extern crate rustc_infer; @@ -43,7 +44,6 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; extern crate rustc_trait_selection; -extern crate rustc_hir_analysis; #[macro_use] extern crate clippy_utils; @@ -180,6 +180,7 @@ mod bool_assert_comparison; mod bool_to_int_with_if; mod booleans; mod borrow_deref_ref; +mod box_default; mod cargo; mod casts; mod checked_conversions; @@ -198,12 +199,12 @@ mod default_union_representation; mod dereference; mod derivable_impls; mod derive; +mod disallowed_macros; mod disallowed_methods; mod disallowed_names; mod disallowed_script_idents; mod disallowed_types; mod doc; -mod doc_link_with_quotes; mod double_parens; mod drop_forget_ref; mod duplicate_mod; @@ -238,6 +239,7 @@ mod if_not_else; mod if_then_some_else_none; mod implicit_hasher; mod implicit_return; +mod implicit_saturating_add; mod implicit_saturating_sub; mod inconsistent_struct_constructor; mod index_refutable_slice; @@ -267,6 +269,7 @@ mod main_recursion; mod manual_assert; mod manual_async_fn; mod manual_bits; +mod manual_clamp; mod manual_instant_elapsed; mod manual_non_exhaustive; mod manual_rem_euclid; @@ -416,8 +419,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { sess.err(&format!( - "error reading Clippy's configuration file. `{}` is not a valid Rust version", - s + "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None }) @@ -433,8 +435,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { let clippy_msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { sess.err(&format!( - "error reading Clippy's configuration file. `{}` is not a valid Rust version", - s + "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None }) @@ -445,8 +446,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { // if both files have an msrv, let's compare them and emit a warning if they differ if clippy_msrv != cargo_msrv { sess.warn(&format!( - "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{}` from `clippy.toml`", - clippy_msrv + "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" )); } @@ -465,7 +465,7 @@ pub fn read_conf(sess: &Session) -> Conf { Ok(Some(path)) => path, Ok(None) => return Conf::default(), Err(error) => { - sess.struct_err(&format!("error finding Clippy's configuration file: {}", error)) + sess.struct_err(&format!("error finding Clippy's configuration file: {error}")) .emit(); return Conf::default(); }, @@ -535,9 +535,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new())); store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle)); store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths)); - store.register_late_pass(|_| Box::new(utils::internal_lints::InterningDefinedSymbol::default())); - store.register_late_pass(|_| Box::new(utils::internal_lints::LintWithoutLintPass::default())); - store.register_late_pass(|_| Box::new(utils::internal_lints::MatchTypeOnDiagItem)); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath)); store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass)); store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl)); } @@ -629,10 +629,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: msrv, )) }); - store.register_late_pass(|_| Box::new(shadow::Shadow::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); store.register_late_pass(|_| Box::new(loops::Loops)); - store.register_late_pass(|_| Box::new(main_recursion::MainRecursion::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|_| Box::new(entry::HashMapPass)); store.register_late_pass(|_| Box::new(minmax::MinMaxPass)); @@ -666,7 +666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(format::UselessFormat)); store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional)); - store.register_late_pass(|_| Box::new(new_without_default::NewWithoutDefault::default())); + store.register_late_pass(|_| Box::::default()); let disallowed_names = conf.disallowed_names.iter().cloned().collect::>(); store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone()))); let too_many_arguments_threshold = conf.too_many_arguments_threshold; @@ -705,7 +705,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef)); store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)); - store.register_late_pass(|_| Box::new(useless_conversion::UselessConversion::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher)); store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom)); store.register_late_pass(|_| Box::new(question_mark::QuestionMark)); @@ -775,7 +775,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: upper_case_acronyms_aggressive, )) }); - store.register_late_pass(|_| Box::new(default::Default::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api))); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); @@ -798,7 +798,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); - store.register_late_pass(|_| Box::new(redundant_pub_crate::RedundantPubCrate::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv))); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); @@ -816,11 +816,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::>(); store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher))); - store.register_late_pass(|_| Box::new(macro_use::MacroUseImports::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); + let disallowed_macros = conf.disallowed_macros.clone(); + store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone()))); let disallowed_methods = conf.disallowed_methods.clone(); store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); @@ -829,7 +831,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(strings::StrToString)); store.register_late_pass(|_| Box::new(strings::StringToString)); store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)); - store.register_late_pass(|_| Box::new(vec_init_then_push::VecInitThenPush::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); @@ -857,7 +859,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(move |_| Box::new(format_args::FormatArgs)); + store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); @@ -866,8 +868,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); - store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); - store.register_late_pass(|_| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default())); + store.register_late_pass(|_| Box::::default()); let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; @@ -876,7 +877,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ignore_publish: cargo_ignore_publish, }) }); - store.register_late_pass(|_| Box::new(write::Write::default())); + store.register_late_pass(|_| Box::::default()); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); @@ -886,7 +887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); - store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); + store.register_early_pass(|| Box::::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); @@ -898,13 +899,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); - store.register_late_pass(|_| Box::new(std_instead_of_core::StdReexports::default())); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed)); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); + store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)); + store.register_late_pass(|_| Box::new(box_default::BoxDefault)); + store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 399a03187d99..aef253303a8f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -9,8 +9,8 @@ use rustc_hir::intravisit::{ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, - TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, TraitFn, + TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter as middle_nested_filter; @@ -276,7 +276,7 @@ fn could_use_elision<'tcx>( let mut checker = BodyLifetimeChecker { lifetimes_used_in_body: false, }; - checker.visit_expr(&body.value); + checker.visit_expr(body.value); if checker.lifetimes_used_in_body { return false; } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index fb2104861c87..25f19b9c6e6c 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -478,7 +478,7 @@ impl DecimalLiteralRepresentation { if num_lit.radix == Radix::Decimal; if val >= u128::from(self.threshold); then { - let hex = format!("{:#X}", val); + let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { warning_type.display(num_lit.format(), cx, lit.span); diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 8e3ab26a947f..14f223481327 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -44,11 +44,10 @@ pub(super) fn check<'tcx>( cx, EXPLICIT_COUNTER_LOOP, span, - &format!("the variable `{}` is used as a loop counter", name), + &format!("the variable `{name}` is used as a loop counter"), "consider using", format!( - "for ({}, {}) in {}.enumerate()", - name, + "for ({name}, {}) in {}.enumerate()", snippet_with_applicability(cx, pat.span, "item", &mut applicability), make_iterator_snippet(cx, arg, &mut applicability), ), @@ -65,24 +64,21 @@ pub(super) fn check<'tcx>( cx, EXPLICIT_COUNTER_LOOP, span, - &format!("the variable `{}` is used as a loop counter", name), + &format!("the variable `{name}` is used as a loop counter"), |diag| { diag.span_suggestion( span, "consider using", format!( - "for ({}, {}) in (0_{}..).zip({})", - name, + "for ({name}, {}) in (0_{int_name}..).zip({})", snippet_with_applicability(cx, pat.span, "item", &mut applicability), - int_name, make_iterator_snippet(cx, arg, &mut applicability), ), applicability, ); diag.note(&format!( - "`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`", - name, int_name + "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" )); }, ); diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 5f5beccd030c..b1f2941622ab 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -41,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, m "it is more concise to loop over references to containers instead of using explicit \ iteration methods", "to write this more concisely, try", - format!("&{}{}", muta, object), + format!("&{muta}{object}"), applicability, ); } diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index bee0e1d76831..ed620460dbe6 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx cx, FOR_KV_MAP, arg_span, - &format!("you seem to want to iterate on a map's {}s", kind), + &format!("you seem to want to iterate on a map's {kind}s"), |diag| { let map = sugg::Sugg::hir(cx, arg, "map"); multispan_sugg( @@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx "use the corresponding method", vec![ (pat_span, snippet(cx, new_pat_span, kind).into_owned()), - (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)), + (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_par())), ], ); }, diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs index 09b2376d5c04..4bb9936e9cde 100644 --- a/clippy_lints/src/loops/manual_find.rs +++ b/clippy_lints/src/loops/manual_find.rs @@ -1,7 +1,7 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FIND; use clippy_utils::{ - diagnostics::span_lint_and_then, higher, is_lang_ctor, path_res, peel_blocks_with_stmt, + diagnostics::span_lint_and_then, higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt, source::snippet_with_applicability, ty::implements_trait, }; use if_chain::if_chain; @@ -30,8 +30,8 @@ pub(super) fn check<'tcx>( if let [stmt] = block.stmts; if let StmtKind::Semi(semi) = stmt.kind; if let ExprKind::Ret(Some(ret_value)) = semi.kind; - if let ExprKind::Call(Expr { kind: ExprKind::Path(ctor), .. }, [inner_ret]) = ret_value.kind; - if is_lang_ctor(cx, ctor, LangItem::OptionSome); + if let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind; + if is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome); if path_res(cx, inner_ret) == Res::Local(binding_id); if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr); then { @@ -143,8 +143,7 @@ fn last_stmt_and_ret<'tcx>( if let Some((_, Node::Block(block))) = parent_iter.next(); if let Some((last_stmt, last_ret)) = extract(block); if last_stmt.hir_id == node_hir; - if let ExprKind::Path(path) = &last_ret.kind; - if is_lang_ctor(cx, path, LangItem::OptionNone); + if is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone); if let Some((_, Node::Expr(_block))) = parent_iter.next(); // This includes the function header if let Some((_, func)) = parent_iter.next(); diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 1d6ddf4b99f7..8c27c09404b1 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -3,13 +3,13 @@ use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_lang_ctor, path_to_local_id, peel_blocks_with_stmt}; +use clippy_utils::{path_to_local_id, peel_blocks_with_stmt}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, DefIdTree}; use rustc_span::source_map::Span; /// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the @@ -30,15 +30,17 @@ pub(super) fn check<'tcx>( if path_to_local_id(let_expr, pat_hir_id); // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind; - let some_ctor = is_lang_ctor(cx, qpath, OptionSome); - let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id); + if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); + let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id); + let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id); if some_ctor || ok_ctor; // Ensure expr in `if let` is not used afterwards if !is_local_used(cx, if_then, pat_hir_id); then { let if_let_type = if some_ctor { "Some" } else { "Ok" }; // Prepare the error message - let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); + let msg = format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used"); // Prepare the help message let mut applicability = Applicability::MaybeIncorrect; diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 3fc569af89ec..c87fc4f90e21 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -177,13 +177,7 @@ fn build_manual_memcpy_suggestion<'tcx>( let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY { dst_base_str } else { - format!( - "{}[{}..{}]", - dst_base_str, - dst_offset.maybe_par(), - dst_limit.maybe_par() - ) - .into() + format!("{dst_base_str}[{}..{}]", dst_offset.maybe_par(), dst_limit.maybe_par()).into() }; let method_str = if is_copy(cx, elem_ty) { @@ -193,10 +187,7 @@ fn build_manual_memcpy_suggestion<'tcx>( }; format!( - "{}.{}(&{}[{}..{}]);", - dst, - method_str, - src_base_str, + "{dst}.{method_str}(&{src_base_str}[{}..{}]);", src_offset.maybe_par(), src_limit.maybe_par() ) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 74f3bda9f43e..c0a0444485e3 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -635,7 +635,7 @@ declare_clippy_lint! { /// arr.into_iter().find(|&el| el == 1) /// } /// ``` - #[clippy::version = "1.61.0"] + #[clippy::version = "1.64.0"] pub MANUAL_FIND, complexity, "manual implementation of `Iterator::find`" diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 6d585c2e45de..0ee42b61c9a5 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -4,11 +4,11 @@ use clippy_utils::{get_enclosing_block, higher, path_to_local}; use if_chain::if_chain; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::{mir::FakeReadCause, ty}; use rustc_span::source_map::Span; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { if_chain! { @@ -114,7 +114,13 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { } } - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } impl MutatePairDelegate<'_, '_> { diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 6e6faa79adc9..66f9e28596e8 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -45,7 +45,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont let (arg, pred) = contains_arg .strip_prefix('&') .map_or(("&x", &*contains_arg), |s| ("x", s)); - format!("any(|{}| x == {})", arg, pred) + format!("any(|{arg}| x == {pred})") } _ => return, } @@ -141,9 +141,9 @@ impl IterFunction { IterFunctionKind::Contains(span) => { let s = snippet(cx, *span, ".."); if let Some(stripped) = s.strip_prefix('&') { - format!(".any(|x| x == {})", stripped) + format!(".any(|x| x == {stripped})") } else { - format!(".any(|x| x == *{})", s) + format!(".any(|x| x == *{s})") } }, } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 8ab640051b63..00cfc6d49f19 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -145,7 +145,7 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), + &format!("the loop variable `{}` is used to index `{indexed}`", ident.name), |diag| { multispan_sugg( diag, @@ -154,7 +154,7 @@ pub(super) fn check<'tcx>( (pat.span, format!("({}, )", ident.name)), ( arg.span, - format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2), + format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), ), ], ); @@ -162,16 +162,16 @@ pub(super) fn check<'tcx>( ); } else { let repl = if starts_at_zero && take_is_empty { - format!("&{}{}", ref_mut, indexed) + format!("&{ref_mut}{indexed}") } else { - format!("{}.{}(){}{}", indexed, method, method_1, method_2) + format!("{indexed}.{method}(){method_1}{method_2}") }; span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed), + &format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), |diag| { multispan_sugg( diag, diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 116e589cad6f..16b00ad66378 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -42,6 +42,7 @@ pub(super) fn check( } } +#[derive(Copy, Clone)] enum NeverLoopResult { // A break/return always get triggered but not necessarily for the main loop. AlwaysBreak, @@ -51,8 +52,8 @@ enum NeverLoopResult { } #[must_use] -fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult { - match *arg { +fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult { + match arg { NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise, NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, } @@ -92,19 +93,29 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult } fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { - let mut iter = block.stmts.iter().filter_map(stmt_to_expr).chain(block.expr); + let mut iter = block + .stmts + .iter() + .filter_map(stmt_to_expr) + .chain(block.expr.map(|expr| (expr, None))); never_loop_expr_seq(&mut iter, main_loop_id) } -fn never_loop_expr_seq<'a, T: Iterator>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { - es.map(|e| never_loop_expr(e, main_loop_id)) - .fold(NeverLoopResult::Otherwise, combine_seq) +fn never_loop_expr_seq<'a, T: Iterator, Option<&'a Block<'a>>)>>( + es: &mut T, + main_loop_id: HirId, +) -> NeverLoopResult { + es.map(|(e, els)| { + let e = never_loop_expr(e, main_loop_id); + els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id))) + }) + .fold(NeverLoopResult::Otherwise, combine_seq) } -fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { +fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { match stmt.kind { - StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e), - StmtKind::Local(local) => local.init, + StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)), + StmtKind::Local(local) => local.init.map(|init| (init, local.els)), StmtKind::Item(..) => None, } } @@ -139,7 +150,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id), ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. - absorb_break(&never_loop_block(b, main_loop_id)) + absorb_break(never_loop_block(b, main_loop_id)) }, ExprKind::If(e, e2, e3) => { let e1 = never_loop_expr(e, main_loop_id); @@ -211,9 +222,5 @@ fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) let pat_snippet = snippet(cx, pat.span, "_"); let iter_snippet = make_iterator_snippet(cx, iterator, &mut Applicability::Unspecified); - format!( - "if let Some({pat}) = {iter}.next()", - pat = pat_snippet, - iter = iter_snippet - ) + format!("if let Some({pat_snippet}) = {iter_snippet}.next()") } diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index aeefe6e33fbe..07edee46fa65 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -30,10 +30,7 @@ pub(super) fn check<'tcx>( vec.span, "it looks like the same item is being pushed into this Vec", None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), + &format!("try using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), ); } diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index f1f58db80b30..b6f4cf7bbb37 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -5,12 +5,12 @@ use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Symbol}; -use rustc_hir_analysis::hir_ty_to_ty; use std::iter::Iterator; #[derive(Debug, PartialEq, Eq)] @@ -344,9 +344,8 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic _ => arg, }; format!( - "{}.{}()", + "{}.{method_name}()", sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_par(), - method_name, ) }, _ => format!( diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index deb21894f36a..153f97e4e66c 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -3,13 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{ - get_enclosing_loop_or_multi_call_closure, is_refutable, is_trait_method, match_def_path, paths, - visitors::is_res_used, + get_enclosing_loop_or_multi_call_closure, is_refutable, is_res_lang_ctor, is_trait_method, visitors::is_res_used, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; +use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::adjustment::Adjust; @@ -19,9 +18,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern - if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; - if let Res::Def(_, pat_did) = pat_path.res; - if match_def_path(cx, pat_did, &paths::OPTION_SOME); + if let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind; + if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); // check for call to `Iterator::next` if let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind; if method_name.ident.name == sym::next; @@ -67,7 +65,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { expr.span.with_hi(scrutinee_expr.span.hi()), "this loop could be written as a `for` loop", "try", - format!("for {} in {}{}", loop_var, iterator, by_ref), + format!("for {loop_var} in {iterator}{by_ref}"), applicability, ); } diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index d573a1b4fbb5..594f6af76b3d 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -189,9 +189,9 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { let mut suggestions = vec![]; for ((root, span, hir_id), path) in used { if path.len() == 1 { - suggestions.push((span, format!("{}::{}", root, path[0]), hir_id)); + suggestions.push((span, format!("{root}::{}", path[0]), hir_id)); } else { - suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")), hir_id)); + suggestions.push((span, format!("{root}::{{{}}}", path.join(", ")), hir_id)); } } @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { // such as `std::prelude::v1::foo` or some other macro that expands to an import. if self.mac_refs.is_empty() { for (span, import, hir_id) in suggestions { - let help = format!("use {};", import); + let help = format!("use {import};"); span_lint_hir_and_then( cx, MACRO_USE_IMPORTS, diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 26b53ab5d683..825ec84b4a81 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,7 +1,8 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use crate::rustc_lint::LintContext; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{peel_blocks_with_stmt, sugg}; +use clippy_utils::{peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -50,20 +51,36 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { let mut applicability = Applicability::MachineApplicable; let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); let cond = cond.peel_drop_temps(); + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); + if !comments.is_empty() { + comments += "\n"; + } let (cond, not) = match cond.kind { ExprKind::Unary(UnOp::Not, e) => (e, ""), _ => (cond, "!"), }; let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); - span_lint_and_sugg( + // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block + span_lint_and_then( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", - "try", - sugg, - Applicability::MachineApplicable, + |diag| { + // comments can be noisy, do not show them to the user + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability); + diag.span_suggestion( + expr.span, + "try instead", + sugg, + applicability); + } + ); } } diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 754b0e78a148..9a0a26c0991b 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -74,11 +74,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { if let Some(ret_pos) = position_before_rarrow(&header_snip); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { - let help = format!("make the function `async` and {}", ret_sugg); + let help = format!("make the function `async` and {ret_sugg}"); diag.span_suggestion( header_span, &help, - format!("async {}{}", &header_snip[..ret_pos], ret_snip), + format!("async {}{ret_snip}", &header_snip[..ret_pos]), Applicability::MachineApplicable ); @@ -196,7 +196,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, }, _ => { let sugg = "return the output of the future directly"; - snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {}", snip))) + snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {snip}"))) }, } } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs new file mode 100644 index 000000000000..ece4df95505c --- /dev/null +++ b/clippy_lints/src/manual_clamp.rs @@ -0,0 +1,713 @@ +use itertools::Itertools; +use rustc_errors::Diagnostic; +use rustc_hir::{ + def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::sym, Span}; +use std::ops::Deref; + +use clippy_utils::{ + diagnostics::{span_lint_and_then, span_lint_hir_and_then}, + eq_expr_value, get_trait_def_id, + higher::If, + is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, paths, peel_blocks, + peel_blocks_with_stmt, + sugg::Sugg, + ty::implements_trait, + visitors::is_const_evaluatable, + MaybePath, +}; +use rustc_errors::Applicability; + +declare_clippy_lint! { + /// ### What it does + /// Identifies good opportunities for a clamp function from std or core, and suggests using it. + /// + /// ### Why is this bad? + /// clamp is much shorter, easier to read, and doesn't use any control flow. + /// + /// ### Known issue(s) + /// If the clamped variable is NaN this suggestion will cause the code to propagate NaN + /// rather than returning either `max` or `min`. + /// + /// `clamp` functions will panic if `max < min`, `max.is_nan()`, or `min.is_nan()`. + /// Some may consider panicking in these situations to be desirable, but it also may + /// introduce panicking where there wasn't any before. + /// + /// ### Examples + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// if input > max { + /// max + /// } else if input < min { + /// min + /// } else { + /// input + /// } + /// # ; + /// ``` + /// + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// input.max(min).min(max) + /// # ; + /// ``` + /// + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// match input { + /// x if x > max => max, + /// x if x < min => min, + /// x => x, + /// } + /// # ; + /// ``` + /// + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// let mut x = input; + /// if x < min { x = min; } + /// if x > max { x = max; } + /// ``` + /// Use instead: + /// ```rust + /// # let (input, min, max) = (0, -2, 1); + /// input.clamp(min, max) + /// # ; + /// ``` + #[clippy::version = "1.66.0"] + pub MANUAL_CLAMP, + complexity, + "using a clamp pattern instead of the clamp function" +} +impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); + +pub struct ManualClamp { + msrv: Option, +} + +impl ManualClamp { + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +#[derive(Debug)] +struct ClampSuggestion<'tcx> { + params: InputMinMax<'tcx>, + span: Span, + make_assignment: Option<&'tcx Expr<'tcx>>, + hir_with_ignore_attr: Option, +} + +#[derive(Debug)] +struct InputMinMax<'tcx> { + input: &'tcx Expr<'tcx>, + min: &'tcx Expr<'tcx>, + max: &'tcx Expr<'tcx>, + is_float: bool, +} + +impl<'tcx> LateLintPass<'tcx> for ManualClamp { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if !meets_msrv(self.msrv, msrvs::CLAMP) { + return; + } + if !expr.span.from_expansion() { + let suggestion = is_if_elseif_else_pattern(cx, expr) + .or_else(|| is_max_min_pattern(cx, expr)) + .or_else(|| is_call_max_min_pattern(cx, expr)) + .or_else(|| is_match_pattern(cx, expr)) + .or_else(|| is_if_elseif_pattern(cx, expr)); + if let Some(suggestion) = suggestion { + emit_suggestion(cx, &suggestion); + } + } + } + + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if !meets_msrv(self.msrv, msrvs::CLAMP) { + return; + } + for suggestion in is_two_if_pattern(cx, block) { + emit_suggestion(cx, &suggestion); + } + } + extract_msrv_attr!(LateContext); +} + +fn emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggestion<'tcx>) { + let ClampSuggestion { + params: InputMinMax { + input, + min, + max, + is_float, + }, + span, + make_assignment, + hir_with_ignore_attr, + } = suggestion; + let input = Sugg::hir(cx, input, "..").maybe_par(); + let min = Sugg::hir(cx, min, ".."); + let max = Sugg::hir(cx, max, ".."); + let semicolon = if make_assignment.is_some() { ";" } else { "" }; + let assignment = if let Some(assignment) = make_assignment { + let assignment = Sugg::hir(cx, assignment, ".."); + format!("{assignment} = ") + } else { + String::new() + }; + let suggestion = format!("{assignment}{input}.clamp({min}, {max}){semicolon}"); + let msg = "clamp-like pattern without using clamp function"; + let lint_builder = |d: &mut Diagnostic| { + d.span_suggestion(*span, "replace with clamp", suggestion, Applicability::MaybeIncorrect); + if *is_float { + d.note("clamp will panic if max < min, min.is_nan(), or max.is_nan()") + .note("clamp returns NaN if the input is NaN"); + } else { + d.note("clamp will panic if max < min"); + } + }; + if let Some(hir_id) = hir_with_ignore_attr { + span_lint_hir_and_then(cx, MANUAL_CLAMP, *hir_id, *span, msg, lint_builder); + } else { + span_lint_and_then(cx, MANUAL_CLAMP, *span, msg, lint_builder); + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum TypeClampability { + Float, + Ord, +} + +impl TypeClampability { + fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option { + if ty.is_floating_point() { + Some(TypeClampability::Float) + } else if get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) { + Some(TypeClampability::Ord) + } else { + None + } + } + + fn is_float(self) -> bool { + matches!(self, TypeClampability::Float) + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min, max) = (0, -3, 12); +/// +/// if input < min { +/// min +/// } else if input > max { +/// max +/// } else { +/// input +/// } +/// # ; +/// ``` +fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let Some(If { + cond, + then, + r#else: Some(else_if), + }) = If::hir(expr) + && let Some(If { + cond: else_if_cond, + then: else_if_then, + r#else: Some(else_body), + }) = If::hir(peel_blocks(else_if)) + { + let params = is_clamp_meta_pattern( + cx, + &BinaryOp::new(peel_blocks(cond))?, + &BinaryOp::new(peel_blocks(else_if_cond))?, + peel_blocks(then), + peel_blocks(else_if_then), + None, + )?; + // Contents of the else should be the resolved input. + if !eq_expr_value(cx, params.input, peel_blocks(else_body)) { + return None; + } + Some(ClampSuggestion { + params, + span: expr.span, + make_assignment: None, + hir_with_ignore_attr: None, + }) + } else { + None + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min_value, max_value) = (0, -3, 12); +/// +/// input.max(min_value).min(max_value) +/// # ; +/// ``` +fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = &expr.kind + && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord)) + && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind + && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || is_trait_method(cx, receiver, sym::Ord)) + { + let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); + let (min, max) = match (seg_first.ident.as_str(), seg_second.ident.as_str()) { + ("min", "max") => (arg_second, arg_first), + ("max", "min") => (arg_first, arg_second), + _ => return None, + }; + Some(ClampSuggestion { + params: InputMinMax { input, min, max, is_float }, + span: expr.span, + make_assignment: None, + hir_with_ignore_attr: None, + }) + } else { + None + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min_value, max_value) = (0, -3, 12); +/// # use std::cmp::{max, min}; +/// min(max(input, min_value), max_value) +/// # ; +/// ``` +fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + fn segment<'tcx>(cx: &LateContext<'_>, func: &Expr<'tcx>) -> Option> { + match func.kind { + ExprKind::Path(QPath::Resolved(None, path)) => { + let id = path.res.opt_def_id()?; + match cx.tcx.get_diagnostic_name(id) { + Some(sym::cmp_min) => Some(FunctionType::CmpMin), + Some(sym::cmp_max) => Some(FunctionType::CmpMax), + _ if is_diag_trait_item(cx, id, sym::Ord) => { + Some(FunctionType::OrdOrFloat(path.segments.last().expect("infallible"))) + }, + _ => None, + } + }, + ExprKind::Path(QPath::TypeRelative(ty, seg)) => { + matches!(path_res(cx, ty), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg)) + }, + _ => None, + } + } + + enum FunctionType<'tcx> { + CmpMin, + CmpMax, + OrdOrFloat(&'tcx PathSegment<'tcx>), + } + + fn check<'tcx>( + cx: &LateContext<'tcx>, + outer_fn: &'tcx Expr<'tcx>, + inner_call: &'tcx Expr<'tcx>, + outer_arg: &'tcx Expr<'tcx>, + span: Span, + ) -> Option> { + if let ExprKind::Call(inner_fn, [first, second]) = &inner_call.kind + && let Some(inner_seg) = segment(cx, inner_fn) + && let Some(outer_seg) = segment(cx, outer_fn) + { + let (input, inner_arg) = match (is_const_evaluatable(cx, first), is_const_evaluatable(cx, second)) { + (true, false) => (second, first), + (false, true) => (first, second), + _ => return None, + }; + let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); + let (min, max) = match (inner_seg, outer_seg) { + (FunctionType::CmpMin, FunctionType::CmpMax) => (outer_arg, inner_arg), + (FunctionType::CmpMax, FunctionType::CmpMin) => (inner_arg, outer_arg), + (FunctionType::OrdOrFloat(first_segment), FunctionType::OrdOrFloat(second_segment)) => { + match (first_segment.ident.as_str(), second_segment.ident.as_str()) { + ("min", "max") => (outer_arg, inner_arg), + ("max", "min") => (inner_arg, outer_arg), + _ => return None, + } + } + _ => return None, + }; + Some(ClampSuggestion { + params: InputMinMax { input, min, max, is_float }, + span, + make_assignment: None, + hir_with_ignore_attr: None, + }) + } else { + None + } + } + + if let ExprKind::Call(outer_fn, [first, second]) = &expr.kind { + check(cx, outer_fn, first, second, expr.span).or_else(|| check(cx, outer_fn, second, first, expr.span)) + } else { + None + } +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min, max) = (0, -3, 12); +/// +/// match input { +/// input if input > max => max, +/// input if input < min => min, +/// input => input, +/// } +/// # ; +/// ``` +fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let ExprKind::Match(value, [first_arm, second_arm, last_arm], rustc_hir::MatchSource::Normal) = &expr.kind { + // Find possible min/max branches + let minmax_values = |a: &'tcx Arm<'tcx>| { + if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind + && let Some(Guard::If(e)) = a.guard { + Some((e, var_hir_id, a.body)) + } else { + None + } + }; + let (first, first_hir_id, first_expr) = minmax_values(first_arm)?; + let (second, second_hir_id, second_expr) = minmax_values(second_arm)?; + let first = BinaryOp::new(first)?; + let second = BinaryOp::new(second)?; + if let PatKind::Binding(_, binding, _, None) = &last_arm.pat.kind + && path_to_local_id(peel_blocks_with_stmt(last_arm.body), *binding) + && last_arm.guard.is_none() + { + // Proceed as normal + } else { + return None; + } + if let Some(params) = is_clamp_meta_pattern( + cx, + &first, + &second, + first_expr, + second_expr, + Some((*first_hir_id, *second_hir_id)), + ) { + return Some(ClampSuggestion { + params: InputMinMax { + input: value, + min: params.min, + max: params.max, + is_float: params.is_float, + }, + span: expr.span, + make_assignment: None, + hir_with_ignore_attr: None, + }); + } + } + None +} + +/// Targets patterns like +/// +/// ``` +/// # let (input, min, max) = (0, -3, 12); +/// +/// let mut x = input; +/// if x < min { x = min; } +/// if x > max { x = max; } +/// ``` +fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Vec> { + block_stmt_with_last(block) + .tuple_windows() + .filter_map(|(maybe_set_first, maybe_set_second)| { + if let StmtKind::Expr(first_expr) = *maybe_set_first + && let StmtKind::Expr(second_expr) = *maybe_set_second + && let Some(If { cond: first_cond, then: first_then, r#else: None }) = If::hir(first_expr) + && let Some(If { cond: second_cond, then: second_then, r#else: None }) = If::hir(second_expr) + && let ExprKind::Assign( + maybe_input_first_path, + maybe_min_max_first, + _ + ) = peel_blocks_with_stmt(first_then).kind + && let ExprKind::Assign( + maybe_input_second_path, + maybe_min_max_second, + _ + ) = peel_blocks_with_stmt(second_then).kind + && eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) + && let Some(first_bin) = BinaryOp::new(first_cond) + && let Some(second_bin) = BinaryOp::new(second_cond) + && let Some(input_min_max) = is_clamp_meta_pattern( + cx, + &first_bin, + &second_bin, + maybe_min_max_first, + maybe_min_max_second, + None + ) + { + Some(ClampSuggestion { + params: InputMinMax { + input: maybe_input_first_path, + min: input_min_max.min, + max: input_min_max.max, + is_float: input_min_max.is_float, + }, + span: first_expr.span.to(second_expr.span), + make_assignment: Some(maybe_input_first_path), + hir_with_ignore_attr: Some(first_expr.hir_id()), + }) + } else { + None + } + }) + .collect() +} + +/// Targets patterns like +/// +/// ``` +/// # let (mut input, min, max) = (0, -3, 12); +/// +/// if input < min { +/// input = min; +/// } else if input > max { +/// input = max; +/// } +/// ``` +fn is_if_elseif_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { + if let Some(If { + cond, + then, + r#else: Some(else_if), + }) = If::hir(expr) + && let Some(If { + cond: else_if_cond, + then: else_if_then, + r#else: None, + }) = If::hir(peel_blocks(else_if)) + && let ExprKind::Assign( + maybe_input_first_path, + maybe_min_max_first, + _ + ) = peel_blocks_with_stmt(then).kind + && let ExprKind::Assign( + maybe_input_second_path, + maybe_min_max_second, + _ + ) = peel_blocks_with_stmt(else_if_then).kind + { + let params = is_clamp_meta_pattern( + cx, + &BinaryOp::new(peel_blocks(cond))?, + &BinaryOp::new(peel_blocks(else_if_cond))?, + peel_blocks(maybe_min_max_first), + peel_blocks(maybe_min_max_second), + None, + )?; + if !eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) { + return None; + } + Some(ClampSuggestion { + params, + span: expr.span, + make_assignment: Some(maybe_input_first_path), + hir_with_ignore_attr: None, + }) + } else { + None + } +} + +/// `ExprKind::Binary` but more narrowly typed +#[derive(Debug, Clone, Copy)] +struct BinaryOp<'tcx> { + op: BinOpKind, + left: &'tcx Expr<'tcx>, + right: &'tcx Expr<'tcx>, +} + +impl<'tcx> BinaryOp<'tcx> { + fn new(e: &'tcx Expr<'tcx>) -> Option> { + match &e.kind { + ExprKind::Binary(op, left, right) => Some(BinaryOp { + op: op.node, + left, + right, + }), + _ => None, + } + } + + fn flip(&self) -> Self { + Self { + op: match self.op { + BinOpKind::Le => BinOpKind::Ge, + BinOpKind::Lt => BinOpKind::Gt, + BinOpKind::Ge => BinOpKind::Le, + BinOpKind::Gt => BinOpKind::Lt, + other => other, + }, + left: self.right, + right: self.left, + } + } +} + +/// The clamp meta pattern is a pattern shared between many (but not all) patterns. +/// In summary, this pattern consists of two if statements that meet many criteria, +/// - binary operators that are one of [`>`, `<`, `>=`, `<=`]. +/// - Both binary statements must have a shared argument +/// - Which can appear on the left or right side of either statement +/// - The binary operators must define a finite range for the shared argument. To put this in +/// the terms of Rust `std` library, the following ranges are acceptable +/// - `Range` +/// - `RangeInclusive` +/// And all other range types are not accepted. For the purposes of `clamp` it's irrelevant +/// whether the range is inclusive or not, the output is the same. +/// - The result of each if statement must be equal to the argument unique to that if statement. The +/// result can not be the shared argument in either case. +fn is_clamp_meta_pattern<'tcx>( + cx: &LateContext<'tcx>, + first_bin: &BinaryOp<'tcx>, + second_bin: &BinaryOp<'tcx>, + first_expr: &'tcx Expr<'tcx>, + second_expr: &'tcx Expr<'tcx>, + // This parameters is exclusively for the match pattern. + // It exists because the variable bindings used in that pattern + // refer to the variable bound in the match arm, not the variable + // bound outside of it. Fortunately due to context we know this has to + // be the input variable, not the min or max. + input_hir_ids: Option<(HirId, HirId)>, +) -> Option> { + fn check<'tcx>( + cx: &LateContext<'tcx>, + first_bin: &BinaryOp<'tcx>, + second_bin: &BinaryOp<'tcx>, + first_expr: &'tcx Expr<'tcx>, + second_expr: &'tcx Expr<'tcx>, + input_hir_ids: Option<(HirId, HirId)>, + is_float: bool, + ) -> Option> { + match (&first_bin.op, &second_bin.op) { + (BinOpKind::Ge | BinOpKind::Gt, BinOpKind::Le | BinOpKind::Lt) => { + let (min, max) = (second_expr, first_expr); + let refers_to_input = match input_hir_ids { + Some((first_hir_id, second_hir_id)) => { + path_to_local_id(peel_blocks(first_bin.left), first_hir_id) + && path_to_local_id(peel_blocks(second_bin.left), second_hir_id) + }, + None => eq_expr_value(cx, first_bin.left, second_bin.left), + }; + (refers_to_input + && eq_expr_value(cx, first_bin.right, first_expr) + && eq_expr_value(cx, second_bin.right, second_expr)) + .then_some(InputMinMax { + input: first_bin.left, + min, + max, + is_float, + }) + }, + _ => None, + } + } + // First filter out any expressions with side effects + let exprs = [ + first_bin.left, + first_bin.right, + second_bin.left, + second_bin.right, + first_expr, + second_expr, + ]; + let clampability = TypeClampability::is_clampable(cx, cx.typeck_results().expr_ty(first_expr))?; + let is_float = clampability.is_float(); + if exprs.iter().any(|e| peel_blocks(e).can_have_side_effects()) { + return None; + } + if !(is_ord_op(first_bin.op) && is_ord_op(second_bin.op)) { + return None; + } + let cases = [ + (*first_bin, *second_bin), + (first_bin.flip(), second_bin.flip()), + (first_bin.flip(), *second_bin), + (*first_bin, second_bin.flip()), + ]; + + cases.into_iter().find_map(|(first, second)| { + check(cx, &first, &second, first_expr, second_expr, input_hir_ids, is_float).or_else(|| { + check( + cx, + &second, + &first, + second_expr, + first_expr, + input_hir_ids.map(|(l, r)| (r, l)), + is_float, + ) + }) + }) +} + +fn block_stmt_with_last<'tcx>(block: &'tcx Block<'tcx>) -> impl Iterator> { + block + .stmts + .iter() + .map(|s| MaybeBorrowedStmtKind::Borrowed(&s.kind)) + .chain( + block + .expr + .as_ref() + .map(|e| MaybeBorrowedStmtKind::Owned(StmtKind::Expr(e))), + ) +} + +fn is_ord_op(op: BinOpKind) -> bool { + matches!(op, BinOpKind::Ge | BinOpKind::Gt | BinOpKind::Le | BinOpKind::Lt) +} + +/// Really similar to Cow, but doesn't have a `Clone` requirement. +#[derive(Debug)] +enum MaybeBorrowedStmtKind<'a> { + Borrowed(&'a StmtKind<'a>), + Owned(StmtKind<'a>), +} + +impl<'a> Clone for MaybeBorrowedStmtKind<'a> { + fn clone(&self) -> Self { + match self { + Self::Borrowed(t) => Self::Borrowed(t), + Self::Owned(StmtKind::Expr(e)) => Self::Owned(StmtKind::Expr(e)), + Self::Owned(_) => unreachable!("Owned should only ever contain a StmtKind::Expr."), + } + } +} + +impl<'a> Deref for MaybeBorrowedStmtKind<'a> { + type Target = StmtKind<'a>; + + fn deref(&self) -> &Self::Target { + match self { + Self::Borrowed(t) => t, + Self::Owned(t) => t, + } + } +} diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 53e7565bd33f..6a42275322b4 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -133,7 +133,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { diag.span_suggestion( header_span, "add the attribute", - format!("#[non_exhaustive] {}", snippet), + format!("#[non_exhaustive] {snippet}"), Applicability::Unspecified, ); } @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { diag.span_suggestion( header_span, "add the attribute", - format!("#[non_exhaustive] {}", snippet), + format!("#[non_exhaustive] {snippet}"), Applicability::Unspecified, ); } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 95cc6bdbd8ba..6f25a2ed8e43 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// let x: i32 = 24; /// let rem = x.rem_euclid(4); /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.64.0"] pub MANUAL_REM_EUCLID, complexity, "manually reimplementing `rem_euclid`" diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index f28c37d3dca7..3181bc86d179 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { /// let mut vec = vec![0, 1, 2]; /// vec.retain(|x| x % 2 == 0); /// ``` - #[clippy::version = "1.63.0"] + #[clippy::version = "1.64.0"] pub MANUAL_RETAIN, perf, "`retain()` is simpler and the same functionalitys" @@ -92,7 +92,7 @@ fn check_into_iter( && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER) && let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &into_iter_expr.kind && let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id) - && match_def_path(cx, into_iter_def_id, &paths::CORE_ITER_INTO_ITER) + && cx.tcx.lang_items().require(hir::LangItem::IntoIterIntoIter).ok() == Some(into_iter_def_id) && match_acceptable_type(cx, left_expr, msrv) && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) { suggest(cx, parent_expr, left_expr, target_expr); @@ -153,7 +153,7 @@ fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::E && let [filter_params] = filter_body.params && let Some(sugg) = match filter_params.pat.kind { hir::PatKind::Binding(_, _, filter_param_ident, None) => { - Some(format!("{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), filter_param_ident, snippet(cx, filter_body.value.span, ".."))) + Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, ".."))) }, hir::PatKind::Tuple([key_pat, value_pat], _) => { make_sugg(cx, key_pat, value_pat, left_expr, filter_body) @@ -161,7 +161,7 @@ fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::E hir::PatKind::Ref(pat, _) => { match pat.kind { hir::PatKind::Binding(_, _, filter_param_ident, None) => { - Some(format!("{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), filter_param_ident, snippet(cx, filter_body.value.span, ".."))) + Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, ".."))) }, _ => None } @@ -190,23 +190,19 @@ fn make_sugg( match (&key_pat.kind, &value_pat.kind) { (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Binding(_, _, value_param_ident, None)) => { Some(format!( - "{}.retain(|{}, &mut {}| {})", + "{}.retain(|{key_param_ident}, &mut {value_param_ident}| {})", snippet(cx, left_expr.span, ".."), - key_param_ident, - value_param_ident, snippet(cx, filter_body.value.span, "..") )) }, (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Wild) => Some(format!( - "{}.retain(|{}, _| {})", + "{}.retain(|{key_param_ident}, _| {})", snippet(cx, left_expr.span, ".."), - key_param_ident, snippet(cx, filter_body.value.span, "..") )), (hir::PatKind::Wild, hir::PatKind::Binding(_, _, value_param_ident, None)) => Some(format!( - "{}.retain(|_, &mut {}| {})", + "{}.retain(|_, &mut {value_param_ident}| {})", snippet(cx, left_expr.span, ".."), - value_param_ident, snippet(cx, filter_body.value.span, "..") )), _ => None, diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 7941c8c9c7e3..0976940afac3 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -108,15 +108,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { }; let test_span = expr.span.until(then.span); - span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| { - diag.span_note(test_span, &format!("the {} was tested here", kind_word)); + span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| { + diag.span_note(test_span, &format!("the {kind_word} was tested here")); multispan_sugg( diag, - &format!("try using the `strip_{}` method", kind_word), + &format!("try using the `strip_{kind_word}` method"), vec![(test_span, - format!("if let Some() = {}.strip_{}({}) ", + format!("if let Some() = {}.strip_{kind_word}({}) ", snippet(cx, target_arg.span, ".."), - kind_word, snippet(cx, pattern.span, "..")))] .into_iter().chain(strippings.into_iter().map(|span| (span, "".into()))), ); diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 33d744815299..32da37a862d8 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -131,12 +131,12 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> }, hir::ExprKind::Block(block, _) => { match (block.stmts, block.expr.as_ref()) { - (&[], Some(inner_expr)) => { + ([], Some(inner_expr)) => { // If block only contains an expression, // reduce `{ X }` to `X` reduce_unit_expression(cx, inner_expr) }, - (&[ref inner_stmt], None) => { + ([inner_stmt], None) => { // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { @@ -194,10 +194,7 @@ fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String { #[must_use] fn suggestion_msg(function_type: &str, map_type: &str) -> String { - format!( - "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`", - map_type, function_type - ) + format!("called `map(f)` on an `{map_type}` value where `f` is a {function_type} that returns the unit type `()`") } fn lint_map_unit_fn( diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index 8588ab1ed8db..a020282d234f 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -70,9 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability); let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_path.ident.span), "", &mut applicability); let sugg = format!( - "{} let Ok({}) = {}", - ifwhile, - some_expr_string, + "{ifwhile} let Ok({some_expr_string}) = {}", trimmed_ok.trim().trim_end_matches('.'), ); span_lint_and_sugg( @@ -80,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { MATCH_RESULT_OK, expr.span.with_hi(let_expr.span.hi()), "matching on `Some` with `ok()` is redundant", - &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), + &format!("consider matching on `Ok({some_expr_string})` and removing the call to `ok` instead"), sugg, applicability, ); diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 07021f1bcad8..fd14d868df34 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq}; +use clippy_utils::{ + is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, +}; use if_chain::if_chain; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; @@ -110,7 +112,7 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), _ => false, } } diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index b0198e856d5b..76f5e1c941c7 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -3,8 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; use clippy_utils::{ - can_move_expr_to_closure, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, peel_blocks, - peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, + can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; @@ -144,7 +144,7 @@ fn check<'tcx>( let scrutinee = peel_hir_expr_refs(scrutinee).0; let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({})", scrutinee_str) + format!("({scrutinee_str})") } else { scrutinee_str.into() }; @@ -172,9 +172,9 @@ fn check<'tcx>( }; let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; if some_expr.needs_unsafe_block { - format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip) + format!("|{annotation}{some_binding}| unsafe {{ {expr_snip} }}") } else { - format!("|{}{}| {}", annotation, some_binding, expr_snip) + format!("|{annotation}{some_binding}| {expr_snip}") } } } @@ -183,9 +183,9 @@ fn check<'tcx>( let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; if some_expr.needs_unsafe_block { - format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip) + format!("|{pat_snip}| unsafe {{ {expr_snip} }}") } else { - format!("|{}| {}", pat_snip, expr_snip) + format!("|{pat_snip}| {expr_snip}") } } else { // Refutable bindings and mixed reference annotations can't be handled by `map`. @@ -199,9 +199,9 @@ fn check<'tcx>( "manual implementation of `Option::map`", "try this", if else_pat.is_none() && is_else_clause(cx.tcx, expr) { - format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + format!("{{ {scrutinee_str}{as_ref_str}.map({body_str}) }}") } else { - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) + format!("{scrutinee_str}{as_ref_str}.map({body_str})") }, app, ); @@ -251,9 +251,11 @@ fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: Syn match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { + Some(OptionPat::None) + }, PatKind::TupleStruct(ref qpath, [pattern], _) - if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -272,16 +274,14 @@ fn get_some_expr<'tcx>( ) -> Option> { // TODO: Allow more complex expressions. match expr.kind { - ExprKind::Call( - Expr { - kind: ExprKind::Path(ref qpath), - .. - }, - [arg], - ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }), + ExprKind::Call(callee, [arg]) + if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + { + Some(SomeExpr { + expr: arg, + needs_unsafe_block, + }) + }, ExprKind::Block( Block { stmts: [], @@ -302,5 +302,5 @@ fn get_some_expr<'tcx>( // Checks for the `None` value. fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) + is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) } diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index e1111c80f2fe..587c926dc01c 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -3,12 +3,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{is_lang_ctor, path_to_local_id, sugg}; +use clippy_utils::{is_res_lang_ctor, path_to_local_id, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::LangItem::{OptionNone, ResultErr}; use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; +use rustc_middle::ty::DefIdTree; use rustc_span::sym; use super::MANUAL_UNWRAP_OR; @@ -42,12 +44,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{}::unwrap_or`", ty_name), + &format!("this pattern reimplements `{ty_name}::unwrap_or`"), "replace with", format!( - "{}.unwrap_or({})", - suggestion, - reindented_or_body, + "{suggestion}.unwrap_or({reindented_or_body})", ), Applicability::MachineApplicable, ); @@ -61,15 +61,19 @@ fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&' if arms.iter().all(|arm| arm.guard.is_none()); if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { match arm.pat.kind { - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [pat], _) => - matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), + matches!(pat.kind, PatKind::Wild) + && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr), _ => false, } }); let unwrap_arm = &arms[1 - idx]; if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, unwrap_arm.pat.hir_id); + if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); + if cx.tcx.lang_items().option_some_variant() == Some(variant_id) + || cx.tcx.lang_items().result_ok_variant() == Some(variant_id); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); diff --git a/clippy_lints/src/matches/match_as_ref.rs b/clippy_lints/src/matches/match_as_ref.rs index 91d17f481e2d..2818f030b7a6 100644 --- a/clippy_lints/src/matches/match_as_ref.rs +++ b/clippy_lints/src/matches/match_as_ref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_lang_ctor, peel_blocks}; +use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; @@ -45,13 +45,11 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: cx, MATCH_AS_REF, expr.span, - &format!("use `{}()` instead", suggestion), + &format!("use `{suggestion}()` instead"), "try this", format!( - "{}.{}(){}", + "{}.{suggestion}(){cast}", snippet_with_applicability(cx, ex.span, "_", &mut applicability), - suggestion, - cast, ), applicability, ); @@ -61,18 +59,20 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: // Checks if arm has the form `None => None` fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, LangItem::OptionNone)) + matches!( + arm.pat.kind, + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) + ) } // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if_chain! { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind; - if is_lang_ctor(cx, qpath, LangItem::OptionSome); + if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome); if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind; if let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind; - if let ExprKind::Path(ref some_path) = e.kind; - if is_lang_ctor(cx, some_path, LangItem::OptionSome); + if is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome); if let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; then { diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 34cc082687ec..107fad32393c 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -112,7 +112,7 @@ where .join(" | ") }; let pat_and_guard = if let Some(Guard::If(g)) = first_guard { - format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability)) + format!("{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability)) } else { pat }; @@ -131,10 +131,9 @@ where &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }), "try this", format!( - "{}matches!({}, {})", + "{}matches!({}, {pat_and_guard})", if b0 { "" } else { "!" }, snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), - pat_and_guard, ), applicability, ); diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index d37f44d4a17e..37049f835775 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -134,7 +134,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { diag.span_suggestion( keep_arm.pat.span, "try merging the arm patterns", - format!("{} | {}", keep_pat_snip, move_pat_snip), + format!("{keep_pat_snip} | {move_pat_snip}"), Applicability::MaybeIncorrect, ) .help("or try changing either arm body") diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 5ae4a65acaf3..68682cedf1de 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -75,12 +75,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e Some(AssignmentExpr::Local { span, pat_span }) => ( span, format!( - "let {} = {};\n{}let {} = {};", + "let {} = {};\n{}let {} = {snippet_body};", snippet_with_applicability(cx, bind_names, "..", &mut applicability), snippet_with_applicability(cx, matched_vars, "..", &mut applicability), " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), - snippet_with_applicability(cx, pat_span, "..", &mut applicability), - snippet_body + snippet_with_applicability(cx, pat_span, "..", &mut applicability) ), ), None => { @@ -110,10 +109,8 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e if ex.can_have_side_effects() { let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); let sugg = format!( - "{};\n{}{}", - snippet_with_applicability(cx, ex.span, "..", &mut applicability), - indent, - snippet_body + "{};\n{indent}{snippet_body}", + snippet_with_applicability(cx, ex.span, "..", &mut applicability) ); span_lint_and_sugg( @@ -178,10 +175,10 @@ fn sugg_with_curlies<'a>( let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); if let Some(parent_expr) = get_parent_expr(cx, match_expr) { if let ExprKind::Closure { .. } = parent_expr.kind { - cbrace_end = format!("\n{}}}", indent); + cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the closure indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); + cbrace_start = format!("{{\n{indent}"); } } @@ -190,10 +187,10 @@ fn sugg_with_curlies<'a>( let parent_node_id = cx.tcx.hir().get_parent_node(match_expr.hir_id); if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { if let ExprKind::Match(..) = arm.body.kind { - cbrace_end = format!("\n{}}}", indent); + cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the match indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); + cbrace_start = format!("{{\n{indent}"); } } @@ -204,13 +201,8 @@ fn sugg_with_curlies<'a>( }); format!( - "{}let {} = {};\n{}{}{}{}", - cbrace_start, + "{cbrace_start}let {} = {};\n{indent}{assignment_str}{snippet_body}{cbrace_end}", snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability), - indent, - assignment_str, - snippet_body, - cbrace_end + snippet_with_applicability(cx, matched_vars, "..", applicability) ) } diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 1e80b6cf2d83..6647322caa37 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -118,8 +118,8 @@ fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad MATCH_STR_CASE_MISMATCH, bad_case_span, "this `match` arm has a differing case than its expression", - &format!("consider changing the case of this arm to respect `{}`", method_str), - format!("\"{}\"", suggestion), + &format!("consider changing the case of this arm to respect `{method_str}`"), + format!("\"{suggestion}\""), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs index a3aa2e4b389a..42f1e2629d41 100644 --- a/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/clippy_lints/src/matches/match_wild_err_arm.rs @@ -38,7 +38,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' span_lint_and_note(cx, MATCH_WILD_ERR_ARM, arm.pat.span, - &format!("`Err({})` matches all errors", ident_bind_name), + &format!("`Err({ident_bind_name})` matches all errors"), None, "match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable", ); diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 58ea43e69d9b..c4f6852aedc3 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -3,15 +3,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ - eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over, + eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_res_lang_ctor, over, path_res, peel_blocks_with_stmt, }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_span::sym; -use rustc_hir_analysis::hir_ty_to_ty; pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) { @@ -112,10 +112,7 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool let ret = strip_return(else_expr); let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr); if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) { - if let ExprKind::Path(ref qpath) = ret.kind { - return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); - } - return false; + return is_res_lang_ctor(cx, path_res(cx, ret), OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); } return eq_expr_value(cx, if_let.let_expr, ret); } diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index c89784065b8b..81bebff34c82 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -4,11 +4,12 @@ use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::any_temporaries_need_ordered_drop; -use clippy_utils::{higher, is_lang_ctor, is_trait_method}; +use clippy_utils::{higher, is_trait_method}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::LangItem::{self, OptionSome, OptionNone, PollPending, PollReady, ResultOk, ResultErr}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; @@ -87,15 +88,21 @@ fn find_sugg_for_if_let<'tcx>( } }, PatKind::Path(ref path) => { - let method = if is_lang_ctor(cx, path, OptionNone) { - "is_none()" - } else if is_lang_ctor(cx, path, PollPending) { - "is_pending()" + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, check_pat.hir_id) + && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) + { + let method = if cx.tcx.lang_items().option_none_variant() == Some(variant_id) { + "is_none()" + } else if cx.tcx.lang_items().poll_pending_variant() == Some(variant_id) { + "is_pending()" + } else { + return; + }; + // `None` and `Pending` don't have an inner type. + (method, cx.tcx.types.unit) } else { return; - }; - // `None` and `Pending` don't have an inner type. - (method, cx.tcx.types.unit) + } }, _ => return, }; @@ -138,7 +145,7 @@ fn find_sugg_for_if_let<'tcx>( cx, REDUNDANT_PATTERN_MATCHING, let_pat.span, - &format!("redundant pattern matching, consider using `{}`", good_method), + &format!("redundant pattern matching, consider using `{good_method}`"), |diag| { // if/while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -162,7 +169,7 @@ fn find_sugg_for_if_let<'tcx>( .maybe_par() .to_string(); - diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app); + diag.span_suggestion(span, "try this", format!("{keyword} {sugg}.{good_method}"), app); if needs_drop { diag.note("this will change drop order of the result, as well as all temporaries"); @@ -213,7 +220,6 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { - find_good_method_for_match( cx, arms, @@ -253,12 +259,12 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op cx, REDUNDANT_PATTERN_MATCHING, expr.span, - &format!("redundant pattern matching, consider using `{}`", good_method), + &format!("redundant pattern matching, consider using `{good_method}`"), |diag| { diag.span_suggestion( span, "try this", - format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method), + format!("{}.{good_method}", snippet(cx, result_expr.span, "_")), Applicability::MaybeIncorrect, // snippet ); }, @@ -269,8 +275,8 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op #[derive(Clone, Copy)] enum Item { - Lang(LangItem), - Diag(Symbol, Symbol), + Lang(LangItem), + Diag(Symbol, Symbol), } fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expected_item: Item) -> bool { @@ -285,15 +291,16 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte let ty = cx.typeck_results().pat_ty(pat); if is_type_diagnostic_item(cx, ty, expected_ty) { - let variant = ty.ty_adt_def() + let variant = ty + .ty_adt_def() .expect("struct pattern type is not an ADT") .variant_of_res(cx.qpath_res(path, pat.hir_id)); - return variant.name == expected_variant + return variant.name == expected_variant; } false - } + }, } } @@ -308,20 +315,16 @@ fn find_good_method_for_match<'a>( should_be_left: &'a str, should_be_right: &'a str, ) -> Option<&'a str> { - let pat_left = arms[0].pat; - let pat_right = arms[1].pat; + let first_pat = arms[0].pat; + let second_pat = arms[1].pat; - let body_node_pair = if ( - is_pat_variant(cx, pat_left, path_left, expected_item_left) - ) && ( - is_pat_variant(cx, pat_right, path_right, expected_item_right) - ) { + let body_node_pair = if (is_pat_variant(cx, first_pat, path_left, expected_item_left)) + && (is_pat_variant(cx, second_pat, path_right, expected_item_right)) + { (&arms[0].body.kind, &arms[1].body.kind) - } else if ( - is_pat_variant(cx, pat_left, path_left, expected_item_right) - ) && ( - is_pat_variant(cx, pat_right, path_right, expected_item_left) - ) { + } else if (is_pat_variant(cx, first_pat, path_left, expected_item_right)) + && (is_pat_variant(cx, second_pat, path_right, expected_item_left)) + { (&arms[1].body.kind, &arms[0].body.kind) } else { return None; diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 86a9df034979..85269e533a06 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -50,13 +50,13 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0)); let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy { - format!("let value = *{};\n{}", original, trailing_indent) + format!("let value = *{original};\n{trailing_indent}") } else if found.is_unit_return_val { // If the return value of the expression to be moved is unit, then we don't need to // capture the result in a temporary -- we can just replace it completely with `()`. - format!("{};\n{}", original, trailing_indent) + format!("{original};\n{trailing_indent}") } else { - format!("let value = {};\n{}", original, trailing_indent) + format!("let value = {original};\n{trailing_indent}") }; let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly { diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 56bcdc01fe4d..d496107ffd6b 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -99,23 +99,21 @@ fn report_single_pattern( let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; let sugg = format!( - "if {} == {}{} {}{}", + "if {} == {}{} {}{els_str}", snippet(cx, ex.span, ".."), // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), snippet(cx, arms[0].pat.span, ".."), expr_block(cx, arms[0].body, None, "..", Some(expr.span)), - els_str, ); (msg, sugg) } else { let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; let sugg = format!( - "if let {} = {} {}{}", + "if let {} = {} {}{els_str}", snippet(cx, arms[0].pat.span, ".."), snippet(cx, ex.span, ".."), expr_block(cx, arms[0].body, None, "..", Some(expr.span)), - els_str, ); (msg, sugg) } diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index 663277d11365..c6cba81d8718 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths}; +use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -27,8 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine if let ExprKind::Path(ref match_fun_path) = match_fun.kind; if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); if let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind; - if let ExprKind::Path(ref err_fun_path) = err_fun.kind; - if is_lang_ctor(cx, err_fun_path, ResultErr); + if is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr); if let Some(return_ty) = find_return_type(cx, &expr.kind); then { let prefix; @@ -61,9 +60,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine "return " }; let suggestion = if err_ty == expr_err_ty { - format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) + format!("{ret_prefix}{prefix}{origin_snippet}{suffix}") } else { - format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) + format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}") }; span_lint_and_sugg( diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index cad3ea2a176c..0c4d9f100f7a 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_lang_ctor, meets_msrv, msrvs}; +use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; @@ -102,40 +102,38 @@ impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { - if let ExprKind::Path(ref replacement_qpath) = src.kind { - // Check that second argument is `Option::None` - if is_lang_ctor(cx, replacement_qpath, OptionNone) { - // Since this is a late pass (already type-checked), - // and we already know that the second argument is an - // `Option`, we do not need to check the first - // argument's type. All that's left is to get - // replacee's path. - let replaced_path = match dest.kind { - ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => { - if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind { - replaced_path - } else { - return; - } - }, - ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path, - _ => return, - }; + // Check that second argument is `Option::None` + if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + // Since this is a late pass (already type-checked), + // and we already know that the second argument is an + // `Option`, we do not need to check the first + // argument's type. All that's left is to get + // replacee's path. + let replaced_path = match dest.kind { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => { + if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind { + replaced_path + } else { + return; + } + }, + ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path, + _ => return, + }; - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - MEM_REPLACE_OPTION_WITH_NONE, - expr_span, - "replacing an `Option` with `None`", - "consider `Option::take()` instead", - format!( - "{}.take()", - snippet_with_applicability(cx, replaced_path.span, "", &mut applicability) - ), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MEM_REPLACE_OPTION_WITH_NONE, + expr_span, + "replacing an `Option` with `None`", + "consider `Option::take()` instead", + format!( + "{}.take()", + snippet_with_applicability(cx, replaced_path.span, "", &mut applicability) + ), + applicability, + ); } } @@ -203,10 +201,8 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< return; } // disable lint for Option since it is covered in another lint - if let ExprKind::Path(q) = &src.kind { - if is_lang_ctor(cx, q, OptionNone) { - return; - } + if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) { + return; } if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) { span_lint_and_then( diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 22f5635a5bcc..cc26b0f7fa82 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -85,7 +85,7 @@ pub(crate) trait BindInsteadOfMap { let closure_args_snip = snippet(cx, closure_args_span, ".."); let option_snip = snippet(cx, recv.span, ".."); - let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip); + let note = format!("{option_snip}.{}({closure_args_snip} {some_inner_snip})", Self::GOOD_METHOD_NAME); span_lint_and_sugg( cx, BIND_INSTEAD_OF_MAP, diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 44857d61fef8..2e96346be977 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, BYTES_NTH, expr.span, - &format!("called `.bytes().nth()` on a `{}`", caller_type), + &format!("called `.bytes().nth()` on a `{caller_type}`"), "try", format!( "{}.as_bytes().get({})", diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index b2bc1ad5c9ed..56b7fbb9d4bc 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -33,12 +33,11 @@ pub(super) fn check( cx, lint, info.expr.span, - &format!("you should use the `{}` method", suggest), + &format!("you should use the `{suggest}` method"), "like this", - format!("{}{}.{}({})", + format!("{}{}.{suggest}({})", if info.eq { "" } else { "!" }, snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), - suggest, snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)), applicability, ); diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index b85bfec2b12b..7e808760663a 100644 --- a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -26,12 +26,11 @@ pub(super) fn check<'tcx>( cx, lint, info.expr.span, - &format!("you should use the `{}` method", suggest), + &format!("you should use the `{suggest}` method"), "like this", - format!("{}{}.{}('{}')", + format!("{}{}.{suggest}('{}')", if info.eq { "" } else { "!" }, snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability), - suggest, c.escape_default()), applicability, ); diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 7ab6b84c2074..7c7938dd2e8b 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -49,8 +49,7 @@ pub(super) fn check( expr.span, &format!( "using `clone` on a double-reference; \ - this will copy the reference of type `{}` instead of cloning the inner type", - ty + this will copy the reference of type `{ty}` instead of cloning the inner type" ), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { @@ -62,11 +61,11 @@ pub(super) fn check( } let refs = "&".repeat(n + 1); let derefs = "*".repeat(n); - let explicit = format!("<{}{}>::clone({})", refs, ty, snip); + let explicit = format!("<{refs}{ty}>::clone({snip})"); diag.span_suggestion( expr.span, "try dereferencing it", - format!("{}({}{}).clone()", refs, derefs, snip.deref()), + format!("{refs}({derefs}{}).clone()", snip.deref()), Applicability::MaybeIncorrect, ); diag.span_suggestion( @@ -121,16 +120,16 @@ pub(super) fn check( let (help, sugg) = if deref_count == 0 { ("try removing the `clone` call", snip.into()) } else if parent_is_suffix_expr { - ("try dereferencing it", format!("({}{})", "*".repeat(deref_count), snip)) + ("try dereferencing it", format!("({}{snip})", "*".repeat(deref_count))) } else { - ("try dereferencing it", format!("{}{}", "*".repeat(deref_count), snip)) + ("try dereferencing it", format!("{}{snip}", "*".repeat(deref_count))) }; span_lint_and_sugg( cx, CLONE_ON_COPY, expr.span, - &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), + &format!("using `clone` on type `{ty}` which implements the `Copy` trait"), help, sugg, app, diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index f82ca8912006..355f53532e26 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -41,7 +41,7 @@ pub(super) fn check( expr.span, "using `.clone()` on a ref-counted pointer", "try this", - format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet), + format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)), Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak ); } diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index bd846d71d466..d0cf411dfd34 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -143,9 +143,9 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - &format!("use of `{}` followed by a function call", name), + &format!("use of `{name}` followed by a function call"), "try this", - format!("unwrap_or_else({} panic!({}))", closure_args, sugg), + format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, ); return; @@ -160,12 +160,9 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - &format!("use of `{}` followed by a function call", name), + &format!("use of `{name}` followed by a function call"), "try this", - format!( - "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})", - closure_args, arg_root_snippet - ), + format!("unwrap_or_else({closure_args} {{ panic!(\"{{}}\", {arg_root_snippet}) }})"), applicability, ); } diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs index 7b2967feb0fe..3fef53739fbd 100644 --- a/clippy_lints/src/methods/filetype_is_file.rs +++ b/clippy_lints/src/methods/filetype_is_file.rs @@ -1,17 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::match_type; -use clippy_utils::{get_parent_expr, paths}; +use clippy_utils::get_parent_expr; +use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; +use rustc_span::sym; use super::FILETYPE_IS_FILE; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv); - if !match_type(cx, ty, &paths::FILE_TYPE) { + if !is_type_diagnostic_item(cx, ty, sym::FileType) { return; } @@ -35,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr span = expr.span; } } - let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb); - let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary); + let lint_msg = format!("`{lint_unary}FileType::is_file()` only {verb} regular files"); + let help_msg = format!("use `{help_unary}FileType::is_dir()` instead"); span_lint_and_help(cx, FILETYPE_IS_FILE, span, &lint_msg, None, &help_msg); } diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index 38ec4d8e3ab8..ddf8a1f09b87 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try this", - format!("{}.find_map({})", iter_snippet, filter_snippet), + format!("{iter_snippet}.find_map({filter_snippet})"), Applicability::MachineApplicable, ); } else { diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index bcf8d93b602e..edcec0fc1015 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try this", - format!("{}.find({})", iter_snippet, filter_snippet), + format!("{iter_snippet}.find({filter_snippet})"), Applicability::MachineApplicable, ); } else { diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 6436e28a63c5..66dfce3682b5 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -23,7 +23,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp // `expr` implements `FromIterator` trait let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); let turbofish = extract_turbofish(cx, expr, ty); - let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish); + let sugg = format!("{iter_expr}.collect::<{turbofish}>()"); span_lint_and_sugg( cx, FROM_ITER_INSTEAD_OF_COLLECT, @@ -63,7 +63,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> if e == type_specifier { None } else { Some((*e).to_string()) } }).collect::>(); // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) - format!("{}{}", without_ts.join("::"), type_specifier) + format!("{}{type_specifier}", without_ts.join("::")) } else { // type is not explicitly specified so wildcards are needed // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` @@ -72,7 +72,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> let end = ty_str.find('>').unwrap_or(ty_str.len()); let nb_wildcard = ty_str[start..end].split(',').count(); let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{}>", elements.join("::"), wildcards) + format!("{}<{wildcards}>", elements.join("::")) } } } diff --git a/clippy_lints/src/methods/get_first.rs b/clippy_lints/src/methods/get_first.rs index 4de77de74042..cb17af608a3f 100644 --- a/clippy_lints/src/methods/get_first.rs +++ b/clippy_lints/src/methods/get_first.rs @@ -29,9 +29,9 @@ pub(super) fn check<'tcx>( cx, GET_FIRST, expr.span, - &format!("accessing first element with `{0}.get(0)`", slice_name), + &format!("accessing first element with `{slice_name}.get(0)`"), "try", - format!("{}.first()", slice_name), + format!("{slice_name}.first()"), app, ); } diff --git a/clippy_lints/src/methods/get_last_with_len.rs b/clippy_lints/src/methods/get_last_with_len.rs index 02aada87202c..3bdc154df049 100644 --- a/clippy_lints/src/methods/get_last_with_len.rs +++ b/clippy_lints/src/methods/get_last_with_len.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::SpanlessEq; -use rustc_ast::LitKind; +use clippy_utils::{is_integer_literal, SpanlessEq}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -26,8 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: && lhs_path.ident.name == sym::len // RHS of subtraction is 1 - && let ExprKind::Lit(rhs_lit) = &rhs.kind - && let LitKind::Int(1, ..) = rhs_lit.node + && is_integer_literal(rhs, 1) // check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)` && SpanlessEq::new(cx).eq_expr(recv, lhs_recv) diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs index 18e08d6ee232..ffc3a4d780e5 100644 --- a/clippy_lints/src/methods/get_unwrap.rs +++ b/clippy_lints/src/methods/get_unwrap.rs @@ -71,16 +71,11 @@ pub(super) fn check<'tcx>( cx, GET_UNWRAP, span, - &format!( - "called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise", - mut_str, caller_type - ), + &format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"), "try this", format!( - "{}{}[{}]", - borrow_str, - snippet_with_applicability(cx, recv.span, "..", &mut applicability), - get_args_str + "{borrow_str}{}[{get_args_str}]", + snippet_with_applicability(cx, recv.span, "..", &mut applicability) ), applicability, ); diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 9651a52be4e7..429cdc1918d7 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -26,12 +26,12 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv cx, IMPLICIT_CLONE, expr.span, - &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_name), + &format!("implicitly cloning a `{ty_name}` by calling `{method_name}` on its dereferenced type"), "consider using", if ref_count > 1 { - format!("({}{}).clone()", "*".repeat(ref_count - 1), recv_snip) + format!("({}{recv_snip}).clone()", "*".repeat(ref_count - 1)) } else { - format!("{}.clone()", recv_snip) + format!("{recv_snip}.clone()") }, app, ); diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index e1c9b5248a8a..ede3b8bb74e9 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -34,18 +34,17 @@ pub fn check<'tcx>( cx, INEFFICIENT_TO_STRING, expr.span, - &format!("calling `to_string` on `{}`", arg_ty), + &format!("calling `to_string` on `{arg_ty}`"), |diag| { diag.help(&format!( - "`{}` implements `ToString` through a slower blanket impl, but `{}` has a fast specialization of `ToString`", - self_ty, deref_self_ty + "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" )); let mut applicability = Applicability::MachineApplicable; let arg_snippet = snippet_with_applicability(cx, receiver.span, "..", &mut applicability); diag.span_suggestion( expr.span, "try dereferencing the receiver", - format!("({}{}).to_string()", "*".repeat(deref_count), arg_snippet), + format!("({}{arg_snippet}).to_string()", "*".repeat(deref_count)), applicability, ); }, @@ -66,7 +65,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { } if let ty::Adt(adt, substs) = ty.kind() { - match_def_path(cx, adt.did(), &paths::COW) && substs.type_at(1).is_str() + cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && substs.type_at(1).is_str() } else { false } diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index 11e76841e9f0..be56b63506a4 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -30,8 +30,7 @@ pub(super) fn check( INTO_ITER_ON_REF, method_span, &format!( - "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", - method_name, kind, + "this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`", ), "call directly", method_name.to_string(), diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index aa176dcc8b4a..304024e80666 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -37,12 +37,11 @@ pub(super) fn check<'tcx>( cx, IS_DIGIT_ASCII_RADIX, expr.span, - &format!("use of `char::is_digit` with literal radix of {}", num), + &format!("use of `char::is_digit` with literal radix of {num}"), "try", format!( - "{}.{}()", - snippet_with_applicability(cx, self_arg.span, "..", &mut applicability), - replacement + "{}.{replacement}()", + snippet_with_applicability(cx, self_arg.span, "..", &mut applicability) ), applicability, ); diff --git a/clippy_lints/src/methods/iter_cloned_collect.rs b/clippy_lints/src/methods/iter_cloned_collect.rs index 30d56113c6c1..bde6f92b076e 100644 --- a/clippy_lints/src/methods/iter_cloned_collect.rs +++ b/clippy_lints/src/methods/iter_cloned_collect.rs @@ -20,8 +20,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir: cx, ITER_CLONED_COLLECT, to_replace, - &format!("called `iter().{}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ - more readable", method_name), + &format!("called `iter().{method_name}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ + more readable"), "try", ".to_vec()".to_string(), Applicability::MachineApplicable, diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index 052be3d8ee7c..bcddc7c786a5 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -37,7 +37,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E cx, ITER_COUNT, expr.span, - &format!("called `.{}().count()` on a `{}`", iter_method, caller_type), + &format!("called `.{iter_method}().count()` on a `{caller_type}`"), "try", format!( "{}.len()", diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index a7eecabd6849..2244ebfb1292 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -54,9 +54,9 @@ pub(super) fn check<'tcx>( cx, ITER_KV_MAP, expr.span, - &format!("iterating on a map's {}s", replacement_kind), + &format!("iterating on a map's {replacement_kind}s"), "try", - format!("{}.{}{}s()", recv_snippet, into_prefix, replacement_kind), + format!("{recv_snippet}.{into_prefix}{replacement_kind}s()"), applicability, ); } else { @@ -64,9 +64,9 @@ pub(super) fn check<'tcx>( cx, ITER_KV_MAP, expr.span, - &format!("iterating on a map's {}s", replacement_kind), + &format!("iterating on a map's {replacement_kind}s"), "try", - format!("{}.{}{}s().map(|{}| {})", recv_snippet, into_prefix, replacement_kind, binded_ident, + format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{binded_ident}| {})", snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)), applicability, ); diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index b8d1dabe0076..83c1bf203467 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal let suggest = if start_idx == 0 { format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) } else { - format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx) + format!("{}.get({start_idx})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 80ca4c94219f..ceee12784cbb 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -32,8 +32,8 @@ pub(super) fn check<'tcx>( cx, ITER_NTH, expr.span, - &format!("called `.iter{0}().nth()` on a {1}", mut_str, caller_type), + &format!("called `.iter{mut_str}().nth()` on a {caller_type}"), None, - &format!("calling `.get{}()` is both faster and more readable", mut_str), + &format!("calling `.get{mut_str}()` is both faster and more readable"), ); } diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index cea7b0d82ff3..4f73b3ec4224 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate}; +use clippy_utils::{get_expr_use_or_unification_node, is_no_std_crate, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -26,26 +26,11 @@ impl IterType { } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { - let item = match &recv.kind { - ExprKind::Array(v) if v.len() <= 1 => v.first(), - ExprKind::Path(p) => { - if is_lang_ctor(cx, p, OptionNone) { - None - } else { - return; - } - }, - ExprKind::Call(f, some_args) if some_args.len() == 1 => { - if let ExprKind::Path(p) = &f.kind { - if is_lang_ctor(cx, p, OptionSome) { - Some(&some_args[0]) - } else { - return; - } - } else { - return; - } - }, + let item = match recv.kind { + ExprKind::Array([]) => None, + ExprKind::Array([e]) => Some(e), + ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None, + ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, path_res(cx, f), OptionSome) => Some(arg), _ => return, }; let iter_type = match method_name { diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs index a669cbbbcc60..3da230e12d7f 100644 --- a/clippy_lints/src/methods/iter_with_drain.rs +++ b/clippy_lints/src/methods/iter_with_drain.rs @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span cx, ITER_WITH_DRAIN, span.with_hi(expr.span.hi()), - &format!("`drain(..)` used on a `{}`", ty_name), + &format!("`drain(..)` used on a `{ty_name}`"), "try this", "into_iter()".to_string(), Applicability::MaybeIncorrect, diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs index ffd2f4a38b8a..5b758f1e6547 100644 --- a/clippy_lints/src/methods/manual_ok_or.rs +++ b/clippy_lints/src/methods/manual_ok_or.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lang_ctor, path_to_local_id}; +use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; -use rustc_hir::{Closure, Expr, ExprKind, PatKind}; +use rustc_hir::{Expr, ExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -22,8 +22,8 @@ pub(super) fn check<'tcx>( if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option); - if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind; - if is_lang_ctor(cx, err_path, ResultErr); + if let ExprKind::Call(err_path, [err_arg]) = or_expr.kind; + if is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr); if is_ok_wrapping(cx, map_expr); if let Some(recv_snippet) = snippet_opt(cx, recv.span); if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); @@ -37,9 +37,7 @@ pub(super) fn check<'tcx>( "this pattern reimplements `Option::ok_or`", "replace with", format!( - "{}.ok_or({})", - recv_snippet, - reindented_err_arg_snippet + "{recv_snippet}.ok_or({reindented_err_arg_snippet})" ), Applicability::MachineApplicable, ); @@ -48,17 +46,19 @@ pub(super) fn check<'tcx>( } fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { - if let ExprKind::Path(ref qpath) = map_expr.kind { - if is_lang_ctor(cx, qpath, ResultOk) { - return true; - } - } - if_chain! { - if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind; - let body = cx.tcx.hir().body(body); - if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; - if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; - if is_lang_ctor(cx, ok_path, ResultOk); - then { path_to_local_id(ok_arg, param_id) } else { false } + match map_expr.kind { + ExprKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, map_expr.hir_id), ResultOk) => true, + ExprKind::Closure(closure) => { + let body = cx.tcx.hir().body(closure.body); + if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind + && let ExprKind::Call(callee, [ok_arg]) = body.value.kind + && is_res_lang_ctor(cx, path_res(cx, callee), ResultOk) + { + path_to_local_id(ok_arg, param_id) + } else { + false + } + }, + _ => false, } } diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 0fe510beaa07..ec694cf6882e 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -57,11 +57,10 @@ pub fn check( super::MANUAL_SATURATING_ARITHMETIC, expr.span, "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), + &format!("try using `saturating_{arith}`"), format!( - "{}.saturating_{}({})", + "{}.saturating_{arith}({})", snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), ), applicability, diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 46d2fc493f81..8b6b8f1bf16c 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_path_diagnostic_item; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; -use clippy_utils::{is_expr_path_def_path, paths}; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use if_chain::if_chain; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -38,7 +38,7 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { let ty = cx.typeck_results().expr_ty(e); if is_type_diagnostic_item(cx, ty, sym::String) || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str)) - || (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, Ty::is_str)) + || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str)) { Some(RepeatKind::String) } else { @@ -57,7 +57,7 @@ pub(super) fn check( ) { if_chain! { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; - if is_expr_path_def_path(cx, repeat_fn, &paths::ITER_REPEAT); + if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String); if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id); if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); @@ -91,7 +91,7 @@ pub(super) fn check( collect_expr.span, "manual implementation of `str::repeat` using iterators", "try this", - format!("{}.repeat({})", val_str, count_snip), + format!("{val_str}.repeat({count_snip})"), app ) } diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 8261ef5e1ce3..7ce14ec080b1 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -111,11 +111,10 @@ fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_cop MAP_CLONE, replace, message, - &format!("consider calling the dedicated `{}` method", sugg_method), + &format!("consider calling the dedicated `{sugg_method}` method"), format!( - "{}.{}()", + "{}.{sugg_method}()", snippet_with_applicability(cx, root, "..", &mut applicability), - sugg_method, ), applicability, ); diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index 13853dec99de..361ffcb5ef3f 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -20,12 +20,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_ cx, MAP_FLATTEN, expr.span.with_lo(map_span.lo()), - &format!("called `map(..).flatten()` on `{}`", caller_ty_name), - &format!( - "try replacing `map` with `{}` and remove the `.flatten()`", - method_to_use - ), - format!("{}({})", method_to_use, closure_snippet), + &format!("called `map(..).flatten()` on `{caller_ty_name}`"), + &format!("try replacing `map` with `{method_to_use}` and remove the `.flatten()`"), + format!("{method_to_use}({closure_snippet})"), applicability, ); } diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 862a9578e6ff..0f25ef82ed42 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -30,7 +30,7 @@ pub(super) fn check( MAP_IDENTITY, sugg_span, "unnecessary map of the identity function", - &format!("remove the call to `{}`", name), + &format!("remove the call to `{name}`"), String::new(), Applicability::MachineApplicable, ) diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 4a8e7ce4ddbb..74fdead216b0 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -65,7 +65,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try this", - format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), + format!("{var_snippet}.map_or_else({unwrap_snippet}, {map_snippet})"), Applicability::MachineApplicable, ); return true; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 428a354ec6b1..cfcf9596c50d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -109,13 +109,13 @@ use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does @@ -3255,65 +3255,59 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = cx.tcx.type_of(item.def_id); let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); - if_chain! { - if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; - if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next(); - - let method_sig = cx.tcx.fn_sig(impl_item.def_id.def_id); + if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { + let method_sig = cx.tcx.fn_sig(impl_item.def_id); let method_sig = cx.tcx.erase_late_bound_regions(method_sig); - - let first_arg_ty = method_sig.inputs().iter().next(); - - // check conventions w.r.t. conversion method names and predicates - if let Some(first_arg_ty) = first_arg_ty; - - then { - // if this impl block implements a trait, lint in trait definition instead - if !implements_trait && cx.access_levels.is_exported(impl_item.def_id.def_id) { - // check missing trait implementations - for method_config in &TRAIT_METHODS { - if name == method_config.method_name && - sig.decl.inputs.len() == method_config.param_count && - method_config.output_type.matches(&sig.decl.output) && - method_config.self_kind.matches(cx, self_ty, *first_arg_ty) && - fn_header_equals(method_config.fn_header, sig.header) && - method_config.lifetime_param_cond(impl_item) - { - span_lint_and_help( - cx, - SHOULD_IMPLEMENT_TRAIT, - impl_item.span, - &format!( - "method `{}` can be confused for the standard trait method `{}::{}`", - method_config.method_name, - method_config.trait_name, - method_config.method_name - ), - None, - &format!( - "consider implementing the trait `{}` or choosing a less ambiguous method name", - method_config.trait_name - ) - ); - } + let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait && cx.access_levels.is_exported(impl_item.def_id.def_id) { + // check missing trait implementations + for method_config in &TRAIT_METHODS { + if name == method_config.method_name + && sig.decl.inputs.len() == method_config.param_count + && method_config.output_type.matches(&sig.decl.output) + // in case there is no first arg, since we already have checked the number of arguments + // it's should be always true + && first_arg_ty_opt.map_or(true, |first_arg_ty| method_config + .self_kind.matches(cx, self_ty, first_arg_ty) + ) + && fn_header_equals(method_config.fn_header, sig.header) + && method_config.lifetime_param_cond(impl_item) + { + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + &format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, method_config.trait_name, method_config.method_name + ), + None, + &format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ), + ); } } + } - if sig.decl.implicit_self.has_implicit_self() + if sig.decl.implicit_self.has_implicit_self() && !(self.avoid_breaking_exported_api - && cx.access_levels.is_exported(impl_item.def_id.def_id)) + && cx.access_levels.is_exported(impl_item.def_id.def_id)) + && let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next() + && let Some(first_arg_ty) = first_arg_ty_opt { wrong_self_convention::check( cx, name, self_ty, - *first_arg_ty, + first_arg_ty, first_arg.pat.span, implements_trait, false ); } - } } // if this impl block implements a trait, lint in trait definition instead @@ -3799,7 +3793,6 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), - // FIXME: default doesn't work ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true), ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), @@ -3827,7 +3820,7 @@ enum SelfKind { Value, Ref, RefMut, - No, + No, // When we want the first argument type to be different than `Self` } impl SelfKind { diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index c409268de769..6fb92d1c663c 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -98,13 +98,12 @@ pub(super) fn check<'tcx>( format!(".as_ref().map({})", snippet(cx, map_arg.span, "..")) }; let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" }; - let hint = format!("{}.{}()", snippet(cx, as_ref_recv.span, ".."), method_hint); - let suggestion = format!("try using {} instead", method_hint); + let hint = format!("{}.{method_hint}()", snippet(cx, as_ref_recv.span, "..")); + let suggestion = format!("try using {method_hint} instead"); let msg = format!( - "called `{0}` on an Option value. This can be done more directly \ - by calling `{1}` instead", - current_method, hint + "called `{current_method}` on an Option value. This can be done more directly \ + by calling `{hint}` instead" ); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 6657cdccd010..3a23ecc50dc1 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lang_ctor, path_def_id}; +use clippy_utils::{is_res_lang_ctor, path_def_id, path_res}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -51,22 +51,12 @@ pub(super) fn check<'tcx>( return; } - let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind { - is_lang_ctor(cx, qpath, OptionNone) - } else { - return; - }; - - if !default_arg_is_none { + if !is_res_lang_ctor(cx, path_res(cx, def_arg), OptionNone) { // nothing to lint! return; } - let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind { - is_lang_ctor(cx, qpath, OptionSome) - } else { - false - }; + let f_arg_is_some = is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome); if is_option { let self_snippet = snippet(cx, recv.span, ".."); @@ -87,7 +77,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try using `map` instead", - format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet), + format!("{self_snippet}.map({arg_snippet} {func_snippet})"), Applicability::MachineApplicable, ); } @@ -102,7 +92,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try using `and_then` instead", - format!("{0}.and_then({1})", self_snippet, func_snippet), + format!("{self_snippet}.and_then({func_snippet})"), Applicability::MachineApplicable, ); } else if f_arg_is_some { @@ -115,7 +105,7 @@ pub(super) fn check<'tcx>( expr.span, msg, "try using `ok` instead", - format!("{0}.ok()", self_snippet), + format!("{self_snippet}.ok()"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 3c4002a3aef9..30421a6dd5af 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -65,9 +65,8 @@ pub(super) fn check<'tcx>( "map_or(, )" }; let msg = &format!( - "called `map().unwrap_or({})` on an `Option` value. \ - This can be done more directly by calling `{}` instead", - arg, suggest + "called `map().unwrap_or({arg})` on an `Option` value. \ + This can be done more directly by calling `{suggest}` instead" ); span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| { @@ -82,10 +81,10 @@ pub(super) fn check<'tcx>( ]; if !unwrap_snippet_none { - suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{}, ", unwrap_snippet))); + suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, "))); } - diag.multipart_suggestion(&format!("use `{}` instead", suggest), suggestion, applicability); + diag.multipart_suggestion(&format!("use `{suggest}` instead"), suggestion, applicability); }); } } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index b43b9258c471..6a35024d0361 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -62,9 +62,9 @@ pub(super) fn check<'tcx>( cx, OR_FUN_CALL, method_span.with_hi(span.hi()), - &format!("use of `{}` followed by a call to `{}`", name, path), + &format!("use of `{name}` followed by a call to `{path}`"), "try this", - format!("{}()", sugg), + format!("{sugg}()"), Applicability::MachineApplicable, ); @@ -131,7 +131,7 @@ pub(super) fn check<'tcx>( if use_lambda { let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{}| {}", l_arg, snippet).into() + format!("|{l_arg}| {snippet}").into() } else { snippet } @@ -141,9 +141,9 @@ pub(super) fn check<'tcx>( cx, OR_FUN_CALL, span_replace_word, - &format!("use of `{}` followed by a function call", name), + &format!("use of `{name}` followed by a function call"), "try this", - format!("{}_{}({})", name, suffix, sugg), + format!("{name}_{suffix}({sugg})"), Applicability::HasPlaceholders, ); } diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index be5768c35450..55ba6e262df7 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -1,6 +1,6 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{diagnostics::span_lint_and_sugg, is_lang_ctor}; +use clippy_utils::{diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::{lang_items::LangItem, Expr, ExprKind}; use rustc_lint::LateContext; @@ -58,8 +58,7 @@ pub(super) fn check<'tcx>( fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option { if let ExprKind::Call(some_expr, [arg]) = expr.kind - && let ExprKind::Path(qpath) = &some_expr.kind - && is_lang_ctor(cx, qpath, item) + && is_res_lang_ctor(cx, path_res(cx, some_expr), item) { Some(arg.span) } else { diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 7572ba3fe9a9..324c9c17b5a9 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -30,10 +30,7 @@ pub(super) fn check<'tcx>( let option_check_method = if is_some { "is_some" } else { "is_none" }; // lint if caller of search is an Iterator if is_trait_method(cx, is_some_recv, sym::Iterator) { - let msg = format!( - "called `{}()` after searching an `Iterator` with `{}`", - option_check_method, search_method - ); + let msg = format!("called `{option_check_method}()` after searching an `Iterator` with `{search_method}`"); let search_snippet = snippet(cx, search_arg.span, ".."); if search_snippet.lines().count() <= 1 { // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` @@ -86,8 +83,7 @@ pub(super) fn check<'tcx>( &msg, "use `!_.any()` instead", format!( - "!{}.any({})", - iter, + "!{iter}.any({})", any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) ), applicability, @@ -119,7 +115,7 @@ pub(super) fn check<'tcx>( if is_string_or_str_slice(search_recv); if is_string_or_str_slice(search_arg); then { - let msg = format!("called `{}()` after calling `find()` on a string", option_check_method); + let msg = format!("called `{option_check_method}()` after calling `find()` on a string"); match option_check_method { "is_some" => { let mut applicability = Applicability::MachineApplicable; @@ -130,7 +126,7 @@ pub(super) fn check<'tcx>( method_span.with_hi(expr.span.hi()), &msg, "use `contains()` instead", - format!("contains({})", find_arg), + format!("contains({find_arg})"), applicability, ); }, @@ -144,7 +140,7 @@ pub(super) fn check<'tcx>( expr.span, &msg, "use `!_.contains()` instead", - format!("!{}.contains({})", string, find_arg), + format!("!{string}.contains({find_arg})"), applicability, ); }, diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs index 18b6b5be175d..44a7ad394fa0 100644 --- a/clippy_lints/src/methods/single_char_insert_string.rs +++ b/clippy_lints/src/methods/single_char_insert_string.rs @@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); - let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); + let sugg = format!("{base_string_snippet}.insert({pos_arg}, {extension_string})"); span_lint_and_sugg( cx, SINGLE_CHAR_ADD_STR, diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs index 9ea6751956ab..0698bd6a0c52 100644 --- a/clippy_lints/src/methods/single_char_push_string.rs +++ b/clippy_lints/src/methods/single_char_push_string.rs @@ -13,7 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability) { let base_string_snippet = snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); - let sugg = format!("{}.push({})", base_string_snippet, extension_string); + let sugg = format!("{base_string_snippet}.push({extension_string})"); span_lint_and_sugg( cx, SINGLE_CHAR_ADD_STR, diff --git a/clippy_lints/src/methods/stable_sort_primitive.rs b/clippy_lints/src/methods/stable_sort_primitive.rs index 91951c65bb30..09c8ca4cbe44 100644 --- a/clippy_lints/src/methods/stable_sort_primitive.rs +++ b/clippy_lints/src/methods/stable_sort_primitive.rs @@ -17,11 +17,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx cx, STABLE_SORT_PRIMITIVE, e.span, - &format!("used `sort` on primitive type `{}`", slice_type), + &format!("used `sort` on primitive type `{slice_type}`"), |diag| { let mut app = Applicability::MachineApplicable; let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0; - diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app); + diag.span_suggestion(e.span, "try", format!("{recv_snip}.sort_unstable()"), app); diag.note( "an unstable sort typically performs faster without any observable difference for this data type", ); diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 9ca4d65550d3..ae3594bd36c3 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -2,11 +2,11 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::visitors::expr_visitor; +use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::Visitor; use rustc_hir::{ BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, }; @@ -211,7 +211,7 @@ fn indirect_usage<'tcx>( binding: HirId, ctxt: SyntaxContext, ) -> Option> { - if let StmtKind::Local(Local { + if let StmtKind::Local(&Local { pat: Pat { kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None), .. @@ -222,14 +222,12 @@ fn indirect_usage<'tcx>( }) = stmt.kind { let mut path_to_binding = None; - expr_visitor(cx, |expr| { - if path_to_local_id(expr, binding) { - path_to_binding = Some(expr); + let _: Option = for_each_expr_with_closures(cx, init_expr, |e| { + if path_to_local_id(e, binding) { + path_to_binding = Some(e); } - - path_to_binding.is_none() - }) - .visit_expr(init_expr); + ControlFlow::Continue(Descend::from(path_to_binding.is_none())) + }); let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id); let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?; @@ -250,7 +248,7 @@ fn indirect_usage<'tcx>( .. } = iter_usage { - if parent_id == *local_hir_id { + if parent_id == local_hir_id { return Some(IndirectUsage { name: ident.name, span: stmt.span, diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 143dcee35052..6974260f70db 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -34,9 +34,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr "calling `.extend(_.chars())`", "try this", format!( - "{}.push_str({}{})", + "{}.push_str({ref_str}{})", snippet_with_applicability(cx, recv.span, "..", &mut applicability), - ref_str, snippet_with_applicability(cx, target.span, "..", &mut applicability) ), applicability, diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index 55567d8625e5..219a9edd6576 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -24,10 +24,10 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se } let (msg, note_msg) = if count == 0 { - (format!("`{}` called with `0` splits", method_name), + (format!("`{method_name}` called with `0` splits"), "the resulting iterator will always return `None`") } else { - (format!("`{}` called with `1` split", method_name), + (format!("`{method_name}` called with `1` split"), if self_ty.is_slice() { "the resulting iterator will always return the entire slice followed by `None`" } else { diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index 6b306fbf0085..15c1c618c513 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -24,9 +24,9 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - cx, SUSPICIOUS_TO_OWNED, expr.span, - &format!("this `to_owned` call clones the {0} itself and does not cause the {0} contents to become owned", input_type), + &format!("this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned"), "consider using, depending on intent", - format!("{0}.clone()` or `{0}.into_owned()", recv_snip), + format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"), app, ); return true; diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 4e8c201f470b..1cef6226ad4f 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -2,9 +2,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; -use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id}; +use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; +use core::ops::ControlFlow; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -13,7 +14,7 @@ use rustc_span::sym; use super::UNNECESSARY_FILTER_MAP; use super::UNNECESSARY_FIND_MAP; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, name: &str) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, name: &str) { if !is_trait_method(cx, expr, sym::Iterator) { return; } @@ -26,10 +27,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value); - let mut return_visitor = ReturnVisitor::new(cx, arg_id); - return_visitor.visit_expr(body.value); - found_mapping |= return_visitor.found_mapping; - found_filtering |= return_visitor.found_filtering; + let _: Option = for_each_expr(body.value, |e| { + if let hir::ExprKind::Ret(Some(e)) = &e.kind { + let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e); + found_mapping |= found_mapping_res; + found_filtering |= found_filtering_res; + ControlFlow::Continue(Descend::No) + } else { + ControlFlow::Continue(Descend::Yes) + } + }); let in_ty = cx.typeck_results().node_type(body.params[0].hir_id); let sugg = if !found_filtering { @@ -54,22 +61,20 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< UNNECESSARY_FIND_MAP }, expr.span, - &format!("this `.{}` can be written more simply using `.{}`", name, sugg), + &format!("this `.{name}` can be written more simply using `.{sugg}`"), ); } } // returns (found_mapping, found_filtering) fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { - match &expr.kind { + match expr.kind { hir::ExprKind::Call(func, args) => { - if let hir::ExprKind::Path(ref path) = func.kind { - if is_lang_ctor(cx, path, OptionSome) { - if path_to_local_id(&args[0], arg_id) { - return (false, false); - } - return (true, false); + if is_res_lang_ctor(cx, path_res(cx, func), OptionSome) { + if path_to_local_id(&args[0], arg_id) { + return (false, false); } + return (true, false); } (true, true) }, @@ -80,7 +85,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc hir::ExprKind::Match(_, arms, _) => { let mut found_mapping = false; let mut found_filtering = false; - for arm in *arms { + for arm in arms { let (m, f) = check_expression(cx, arg_id, arm.body); found_mapping |= m; found_filtering |= f; @@ -93,39 +98,9 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) }, - hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true), + hir::ExprKind::Path(ref path) if is_res_lang_ctor(cx, cx.qpath_res(path, expr.hir_id), OptionNone) => { + (false, true) + }, _ => (true, true), } } - -struct ReturnVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - arg_id: hir::HirId, - // Found a non-None return that isn't Some(input) - found_mapping: bool, - // Found a return that isn't Some - found_filtering: bool, -} - -impl<'a, 'tcx> ReturnVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> { - ReturnVisitor { - cx, - arg_id, - found_mapping: false, - found_filtering: false, - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if let hir::ExprKind::Ret(Some(expr)) = &expr.kind { - let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr); - self.found_mapping |= found_mapping; - self.found_filtering |= found_filtering; - } else { - walk_expr(self, expr); - } - } -} diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index c17ef6809f91..aa87dead38f0 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -49,15 +49,12 @@ pub(super) fn check( let mut applicability = Applicability::MachineApplicable; let sugg = if replacement_has_args { format!( - "{replacement}(|{s}| {r})", - replacement = replacement_method_name, - s = second_arg_ident, + "{replacement_method_name}(|{second_arg_ident}| {r})", r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), ) } else { format!( - "{replacement}()", - replacement = replacement_method_name, + "{replacement_method_name}()", ) }; diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 95138c0e25b0..1966a85f7a73 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -68,7 +68,7 @@ pub fn check_for_loop_iter( cx, UNNECESSARY_TO_OWNED, expr.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), |diag| { // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to // a `to_owned`-like function was removed, then the next suggestion may be diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index a187a8d6016f..0e73459ad65f 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eager_or_lazy, usage}; +use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -18,6 +18,10 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, simplify_using: &str, ) { + if is_from_proc_macro(cx, expr) { + return; + } + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); @@ -58,8 +62,8 @@ pub(super) fn check<'tcx>( span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { diag.span_suggestion( span, - &format!("use `{}(..)` instead", simplify_using), - format!("{}({})", simplify_using, snippet(cx, body_expr.span, "..")), + &format!("use `{simplify_using}(..)` instead"), + format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), applicability, ); }); diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 559f32a563ed..9ab0d6141146 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -8,6 +8,7 @@ use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trai use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; +use rustc_hir_analysis::check::{FnCtxt, Inherited}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; @@ -18,7 +19,6 @@ use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitP use rustc_semver::RustcVersion; use rustc_span::{sym, Symbol}; use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; -use rustc_hir_analysis::check::{FnCtxt, Inherited}; use std::cmp::max; use super::UNNECESSARY_TO_OWNED; @@ -132,12 +132,11 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", format!( - "{:&>width$}{}", + "{:&>width$}{receiver_snippet}", "", - receiver_snippet, width = n_target_refs - n_receiver_refs ), Applicability::MachineApplicable, @@ -154,7 +153,7 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", receiver_snippet, Applicability::MachineApplicable, @@ -164,7 +163,7 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, expr.span.with_lo(receiver.span.hi()), - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "remove this", String::new(), Applicability::MachineApplicable, @@ -181,9 +180,9 @@ fn check_addr_of_expr( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", - format!("{}.as_ref()", receiver_snippet), + format!("{receiver_snippet}.as_ref()"), Applicability::MachineApplicable, ); return true; @@ -228,9 +227,9 @@ fn check_into_iter_call_arg( cx, UNNECESSARY_TO_OWNED, parent.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", - format!("{}.iter().{}()", receiver_snippet, cloned_or_copied), + format!("{receiver_snippet}.iter().{cloned_or_copied}()"), Applicability::MaybeIncorrect, ); return true; @@ -275,9 +274,9 @@ fn check_other_call_arg<'tcx>( cx, UNNECESSARY_TO_OWNED, maybe_arg.span, - &format!("unnecessary use of `{}`", method_name), + &format!("unnecessary use of `{method_name}`"), "use", - format!("{:&>width$}{}", "", receiver_snippet, width = n_refs), + format!("{:&>n_refs$}{receiver_snippet}", ""), Applicability::MachineApplicable, ); return true; diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index ca5d33ee8b07..c1139d84e2f4 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; -use clippy_utils::{get_parent_expr, match_trait_method, paths}; +use clippy_utils::{get_parent_expr, is_trait_method}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::USELESS_ASREF; @@ -13,7 +14,7 @@ use super::USELESS_ASREF; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait - if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) { + if is_trait_method(cx, expr, sym::AsRef) || is_trait_method(cx, expr, sym::AsMut) { // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); @@ -35,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, cx, USELESS_ASREF, expr.span, - &format!("this call to `{}` does nothing", call_name), + &format!("this call to `{call_name}` does nothing"), "try this", snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), applicability, diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 4b368d3ffae2..1fbf783b8860 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -61,20 +61,20 @@ impl Convention { impl fmt::Display for Convention { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - Self::Eq(this) => format!("`{}`", this).fmt(f), - Self::StartsWith(this) => format!("`{}*`", this).fmt(f), - Self::EndsWith(this) => format!("`*{}`", this).fmt(f), - Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f), + Self::Eq(this) => format!("`{this}`").fmt(f), + Self::StartsWith(this) => format!("`{this}*`").fmt(f), + Self::EndsWith(this) => format!("`*{this}`").fmt(f), + Self::NotEndsWith(this) => format!("`~{this}`").fmt(f), Self::IsSelfTypeCopy(is_true) => { format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f) }, Self::ImplementsTrait(is_true) => { let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; - format!("method{} implement{} a trait", negation, s_suffix).fmt(f) + format!("method{negation} implement{s_suffix} a trait").fmt(f) }, Self::IsTraitItem(is_true) => { let suffix = if is_true { " is" } else { " is not" }; - format!("method{} a trait item", suffix).fmt(f) + format!("method{suffix} a trait item").fmt(f) }, } } @@ -138,8 +138,7 @@ pub(super) fn check<'tcx>( WRONG_SELF_CONVENTION, first_arg_span, &format!( - "{} usually take {}", - suggestion, + "{suggestion} usually take {}", &self_kinds .iter() .map(|k| k.description()) diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 4d8579135fc0..4f967755bfa1 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{match_trait_method, paths}; +use clippy_utils::is_trait_method; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -83,7 +83,7 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { - if cx.typeck_results().expr_ty(receiver).is_floating_point() || match_trait_method(cx, expr, &paths::ORD) { + if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) { if path.ident.name == sym!(max) { fetch_const(cx, Some(receiver), args, MinMax::Max) } else if path.ident.name == sym!(min) { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index ea245edd7704..516dee20f8b1 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_opt}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -15,7 +14,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{ExpnKind, Span}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, iter_input_pats, last_path_segment, SpanlessEq}; +use clippy_utils::{get_parent_expr, in_constant, is_integer_literal, iter_input_pats, last_path_segment, SpanlessEq}; declare_clippy_lint! { /// ### What it does @@ -178,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { ("", sugg_init.addr()) }; let tyopt = if let Some(ty) = local.ty { - format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) + format!(": &{mutopt}{ty}", ty=snippet(cx, ty.span, "..")) } else { String::new() }; @@ -195,8 +194,6 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { format!( "let {name}{tyopt} = {initref};", name=snippet(cx, name.span, ".."), - tyopt=tyopt, - initref=initref, ), Applicability::MachineApplicable, ); @@ -222,8 +219,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { stmt.span, "replace it with", format!( - "if {} {{ {}; }}", - sugg, + "if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."), ), Applicability::MachineApplicable, // snippet @@ -275,9 +271,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { USED_UNDERSCORE_BINDING, expr.span, &format!( - "used binding `{}` which is prefixed with an underscore. A leading \ - underscore signals that a binding will not be used", - binding + "used binding `{binding}` which is prefixed with an underscore. A leading \ + underscore signals that a binding will not be used" ), ); } @@ -318,8 +313,7 @@ fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool { fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { if_chain! { if let TyKind::Ptr(ref mut_ty) = ty.kind; - if let ExprKind::Lit(ref lit) = e.kind; - if let LitKind::Int(0, _) = lit.node; + if is_integer_literal(e, 0); if !in_constant(cx, e.hir_id); then { let (msg, sugg_fn) = match mut_ty.mutbl { @@ -328,12 +322,12 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) }; let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { - (format!("{}()", sugg_fn), Applicability::MachineApplicable) + (format!("{sugg_fn}()"), Applicability::MachineApplicable) } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { - (format!("{}::<{}>()", sugg_fn, mut_ty_snip), Applicability::MachineApplicable) + (format!("{sugg_fn}::<{mut_ty_snip}>()"), Applicability::MachineApplicable) } else { // `MaybeIncorrect` as type inference may not work with the suggested code - (format!("{}()", sugg_fn), Applicability::MaybeIncorrect) + (format!("{sugg_fn}()"), Applicability::MaybeIncorrect) }; span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); } diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs index 1165c19a0cf0..62c6ca32d31a 100644 --- a/clippy_lints/src/misc_early/literal_suffix.rs +++ b/clippy_lints/src/misc_early/literal_suffix.rs @@ -18,9 +18,9 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s cx, SEPARATED_LITERAL_SUFFIX, lit.span, - &format!("{} type suffix should not be separated by an underscore", sugg_type), + &format!("{sugg_type} type suffix should not be separated by an underscore"), "remove the underscore", - format!("{}{}", &lit_snip[..maybe_last_sep_idx], suffix), + format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), Applicability::MachineApplicable, ); } else { @@ -28,9 +28,9 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s cx, UNSEPARATED_LITERAL_SUFFIX, lit.span, - &format!("{} type suffix should be separated by an underscore", sugg_type), + &format!("{sugg_type} type suffix should be separated by an underscore"), "add an underscore", - format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix), + format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 704918c0b979..c8227ca44505 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -357,9 +357,8 @@ impl EarlyLintPass for MiscEarlyLints { DUPLICATE_UNDERSCORE_ARGUMENT, *correspondence, &format!( - "`{}` already exists, having another argument having almost the same \ - name makes code comprehension and documentation more difficult", - arg_name + "`{arg_name}` already exists, having another argument having almost the same \ + name makes code comprehension and documentation more difficult" ), ); } diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs index fff533167ede..676e5d40bb77 100644 --- a/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { pat.span, "all the struct fields are matched to a wildcard pattern, consider using `..`", None, - &format!("try with `{} {{ .. }}` instead", type_name), + &format!("try with `{type_name} {{ .. }}` instead"), ); return; } @@ -63,7 +63,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { "you matched a field with a wildcard pattern, consider using `..` \ instead", None, - &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + &format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")), ); } } diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 020efeaebf02..6dd76a6531e4 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -91,10 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { let type_name = segment.ident; for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() { if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) { - let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order", - type_name, impl_param_name); - let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params", - type_param_names[i], type_name); + let msg = format!("`{type_name}` has a similarly named generic type parameter `{impl_param_name}` in its declaration, but in a different order"); + let help = format!("try `{}`, or a name that does not conflict with `{type_name}`'s generic params", + type_param_names[i]); span_lint_and_help( cx, MISMATCHING_TYPE_PARAM_ORDER, diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 00376f0d7902..71cc0d0a81cd 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -8,12 +8,12 @@ use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 472195566768..b3f1553cfea9 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -103,7 +103,7 @@ impl MissingDoc { cx, MISSING_DOCS_IN_PRIVATE_ITEMS, sp, - &format!("missing documentation for {} {}", article, desc), + &format!("missing documentation for {article} {desc}"), ); } } diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 3d0a23822838..872679f25ab5 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -58,7 +58,8 @@ impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]); impl LateLintPass<'_> for ImportRename { fn check_crate(&mut self, cx: &LateContext<'_>) { for Rename { path, rename } in &self.conf_renames { - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &path.split("::").collect::>()) { + let segs = path.split("::").collect::>(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, None) { self.renames.insert(id, Symbol::intern(rename)); } } @@ -90,9 +91,7 @@ impl LateLintPass<'_> for ImportRename { "this import should be renamed", "try", format!( - "{} as {}", - import, - name, + "{import} as {name}", ), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 9d5764ac0926..655df5419ac6 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -65,7 +65,7 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, - &format!("missing `#[inline]` for {}", desc), + &format!("missing `#[inline]` for {desc}"), ); } } diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs index 102b9fbae83c..0742943dff2b 100644 --- a/clippy_lints/src/module_style.rs +++ b/clippy_lints/src/module_style.rs @@ -118,13 +118,7 @@ impl EarlyLintPass for ModStyle { SELF_NAMED_MODULE_FILES, Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None), format!("`mod.rs` files are required, found `{}`", path.display()), - |lint| { - lint.help(format!( - "move `{}` to `{}`", - path.display(), - correct.display(), - )) - }, + |lint| lint.help(format!("move `{}` to `{}`", path.display(), correct.display(),)), ); } } diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 084c0d471dde..4547ed7eafc8 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -87,7 +87,7 @@ fn check_arguments<'tcx>( cx, UNNECESSARY_MUT_PASSED, argument.span, - &format!("the {} `{}` doesn't need a mutable reference", fn_kind, name), + &format!("the {fn_kind} `{name}` doesn't need a mutable reference"), ); } }, diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 44fdf84c6df7..d8647a991058 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -56,10 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { cx, DEBUG_ASSERT_WITH_MUT_CALL, span, - &format!( - "do not call a function with mutable arguments inside of `{}!`", - macro_name - ), + &format!("do not call a function with mutable arguments inside of `{macro_name}!`"), ); } } diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index a98577093ed5..09cb53331763 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -84,9 +84,8 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { let msg = format!( - "consider using an `{}` instead of a `Mutex` here; if you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`", - atomic_name + "consider using an `{atomic_name}` instead of a `Mutex` here; if you just want the locking \ + behavior and not the internal type, consider using `Mutex<()>`" ); match *mutex_param.kind() { ty::Uint(t) if t != ty::UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, &msg), diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index b8855e5adbff..10c3ff026b6d 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -1,6 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_with_applicability; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -8,36 +6,26 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// Checks for bindings that destructure a reference and borrow the inner + /// Checks for bindings that needlessly destructure a reference and borrow the inner /// value with `&ref`. /// /// ### Why is this bad? /// This pattern has no effect in almost all cases. /// - /// ### Known problems - /// In some cases, `&ref` is needed to avoid a lifetime mismatch error. - /// Example: - /// ```rust - /// fn foo(a: &Option, b: &Option) { - /// match (a, b) { - /// (None, &ref c) | (&ref c, None) => (), - /// (&Some(ref c), _) => (), - /// }; - /// } - /// ``` - /// /// ### Example /// ```rust /// let mut v = Vec::::new(); - /// # #[allow(unused)] /// v.iter_mut().filter(|&ref a| a.is_empty()); + /// + /// if let &[ref first, ref second] = v.as_slice() {} /// ``` /// /// Use instead: /// ```rust /// let mut v = Vec::::new(); - /// # #[allow(unused)] /// v.iter_mut().filter(|a| a.is_empty()); + /// + /// if let [first, second] = v.as_slice() {} /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_BORROWED_REFERENCE, @@ -54,34 +42,83 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { return; } - if_chain! { - // Only lint immutable refs, because `&mut ref T` may be useful. - if let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind; + // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms + for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) { + let Node::Pat(pat) = node else { break }; + + if matches!(pat.kind, PatKind::Or(_)) { + return; + } + } + + // Only lint immutable refs, because `&mut ref T` may be useful. + let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return }; + match sub_pat.kind { // Check sub_pat got a `ref` keyword (excluding `ref mut`). - if let PatKind::Binding(BindingAnnotation::REF, .., spanned_name, _) = sub_pat.kind; - let parent_id = cx.tcx.hir().get_parent_node(pat.hir_id); - if let Some(parent_node) = cx.tcx.hir().find(parent_id); - then { - // do not recurse within patterns, as they may have other references - // XXXManishearth we can relax this constraint if we only check patterns - // with a single ref pattern inside them - if let Node::Pat(_) = parent_node { - return; + PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { + span_lint_and_then( + cx, + NEEDLESS_BORROWED_REFERENCE, + pat.span, + "this pattern takes a reference on something that is being dereferenced", + |diag| { + // `&ref ident` + // ^^^^^ + let span = pat.span.until(ident.span); + diag.span_suggestion_verbose( + span, + "try removing the `&ref` part", + String::new(), + Applicability::MachineApplicable, + ); + }, + ); + }, + // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]` + PatKind::Slice( + before, + None + | Some(Pat { + kind: PatKind::Wild, .. + }), + after, + ) => { + let mut suggestions = Vec::new(); + + for element_pat in itertools::chain(before, after) { + if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind { + // `&[..., ref ident, ...]` + // ^^^^ + let span = element_pat.span.until(ident.span); + suggestions.push((span, String::new())); + } else { + return; + } } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span, - "this pattern takes a reference on something that is being de-referenced", - |diag| { - let hint = snippet_with_applicability(cx, spanned_name.span, "..", &mut applicability).into_owned(); - diag.span_suggestion( - pat.span, - "try removing the `&ref` part and just keep", - hint, - applicability, - ); - }); - } + + if !suggestions.is_empty() { + span_lint_and_then( + cx, + NEEDLESS_BORROWED_REFERENCE, + pat.span, + "dereferencing a slice pattern where every element takes a reference", + |diag| { + // `&[...]` + // ^ + let span = pat.span.until(sub_pat.span); + suggestions.push((span, String::new())); + + diag.multipart_suggestion( + "try removing the `&` and `ref` parts", + suggestions, + Applicability::MachineApplicable, + ); + }, + ); + } + }, + _ => {}, } } } diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 98a3bce1ff38..6f0e755466e5 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -309,7 +309,7 @@ fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, expr.span, message, None, - &format!("{}\n{}", header, snip), + &format!("{header}\n{snip}"), ); } @@ -322,10 +322,7 @@ fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &' let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); format!( - "{indent}if {} {}\n{indent}{}", - cond_code, - continue_code, - else_code, + "{indent}if {cond_code} {continue_code}\n{indent}{else_code}", indent = " ".repeat(indent_if), ) } @@ -349,7 +346,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span); let snip = snippet_block(cx, span, "..", None).into_owned(); snip.lines() - .map(|line| format!("{}{}", " ".repeat(indent), line)) + .map(|line| format!("{}{line}", " ".repeat(indent))) .collect::>() .join("\n") }) @@ -358,10 +355,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0); format!( - "{indent_if}if {} {}\n{indent}// merged code follows:\n{}\n{indent_if}}}", - cond_code, - block_code, - to_annex, + "{indent_if}if {cond_code} {block_code}\n{indent}// merged code follows:\n{to_annex}\n{indent_if}}}", indent = " ".repeat(indent), indent_if = " ".repeat(indent_if), ) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index de99f1d7078e..9d26e5900866 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -2,9 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; use clippy_utils::ty::needs_ordered_drop; -use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used}; +use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_local_used}; +use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::intravisit::Visitor; use rustc_hir::{ BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, @@ -64,31 +64,25 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]); fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool { - let mut seen = false; - expr_visitor(cx, |expr| { - if let ExprKind::Assign(..) = expr.kind { - seen = true; + for_each_expr_with_closures(cx, stmt, |e| { + if matches!(e.kind, ExprKind::Assign(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - - !seen }) - .visit_stmt(stmt); - - seen + .is_some() } fn contains_let(cond: &Expr<'_>) -> bool { - let mut seen = false; - expr_visitor_no_bodies(|expr| { - if let ExprKind::Let(_) = expr.kind { - seen = true; + for_each_expr(cond, |e| { + if matches!(e.kind, ExprKind::Let(_)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - - !seen }) - .visit_expr(cond); - - seen + .is_some() } fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { @@ -287,7 +281,7 @@ fn check<'tcx>( diag.span_suggestion( assign.lhs_span, - &format!("declare `{}` here", binding_name), + &format!("declare `{binding_name}` here"), let_snippet, Applicability::MachineApplicable, ); @@ -307,8 +301,8 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{}` here", binding_name), - format!("{} = ", let_snippet), + &format!("declare `{binding_name}` here"), + format!("{let_snippet} = "), applicability, ); @@ -338,8 +332,8 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{}` here", binding_name), - format!("{} = ", let_snippet), + &format!("declare `{binding_name}` here"), + format!("{let_snippet} = "), applicability, ); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 4f46872439c3..178c973981b1 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -12,17 +12,17 @@ use rustc_hir::{ BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind, }; use rustc_hir::{HirIdMap, HirIdSet}; +use rustc_hir_analysis::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::kw; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::can_type_implement_copy; -use rustc_hir_analysis::expr_use_visitor as euv; use std::borrow::Cow; declare_clippy_lint! { @@ -186,6 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { if !is_self(arg); if !ty.is_mutable_ptr(); if !is_copy(cx, ty); + if ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env); if !allowed_traits.iter().any(|&t| implements_trait(cx, ty, t, &[])); if !implements_borrow_trait; if !all_borrowable_trait; @@ -236,7 +237,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { snippet_opt(cx, span) .map_or( "change the call to".into(), - |x| Cow::from(format!("change `{}` to", x)), + |x| Cow::from(format!("change `{x}` to")), ) .as_ref(), suggestion, @@ -266,7 +267,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { snippet_opt(cx, span) .map_or( "change the call to".into(), - |x| Cow::from(format!("change `{}` to", x)) + |x| Cow::from(format!("change `{x}` to")) ) .as_ref(), suggestion, @@ -341,5 +342,11 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 8f85b00596c0..97c8cfbd3eb7 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_lang_ctor; +use clippy_utils::path_res; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::DefIdTree; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -112,11 +113,12 @@ impl LateLintPass<'_> for NeedlessQuestionMark { fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Call(path, [arg]) = &expr.kind; - if let ExprKind::Path(ref qpath) = &path.kind; - let sugg_remove = if is_lang_ctor(cx, qpath, OptionSome) { + if let ExprKind::Call(path, [arg]) = expr.kind; + if let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path); + if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); + let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) { "Some()" - } else if is_lang_ctor(cx, qpath, ResultOk) { + } else if cx.tcx.lang_items().result_ok_variant() == Some(variant_id) { "Ok()" } else { return; @@ -134,7 +136,7 @@ fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { NEEDLESS_QUESTION_MARK, expr.span, "question mark operator is useless here", - &format!("try removing question mark and `{}`", sugg_remove), + &format!("try removing question mark and `{sugg_remove}`"), format!("{}", snippet(cx, inner_expr.span, r#""...""#)), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index b087cfb36b11..fb9a4abd0b4b 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -62,9 +62,9 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { let mut applicability = Applicability::MachineApplicable; let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability); let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { - format!("-({})", snip) + format!("-({snip})") } else { - format!("-{}", snip) + format!("-{snip}") }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 357a71693d2c..6017117e1ecc 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -136,8 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { id, impl_item.span, &format!( - "you should consider adding a `Default` implementation for `{}`", - self_type_snip + "you should consider adding a `Default` implementation for `{self_type_snip}`" ), |diag| { diag.suggest_prepend_item( @@ -161,9 +160,9 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { fn create_new_without_default_suggest_msg(self_type_snip: &str, generics_sugg: &str) -> String { #[rustfmt::skip] format!( -"impl{} Default for {} {{ +"impl{generics_sugg} Default for {self_type_snip} {{ fn default() -> Self {{ Self::new() }} -}}", generics_sugg, self_type_snip) +}}") } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 48ff737dae7b..2c839d029c6f 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{ BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, }; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::mir; use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; @@ -20,7 +21,6 @@ use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, InnerSpan, Span, DUMMY_SP}; -use rustc_hir_analysis::hir_ty_to_ty; // FIXME: this is a correctness problem but there's no suitable // warn-by-default category. @@ -149,6 +149,9 @@ fn is_value_unfrozen_raw<'tcx>( // the fact that we have to dig into every structs to search enums // leads us to the point checking `UnsafeCell` directly is the only option. ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true, + // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the + // contained value. + ty::Adt(def, ..) if def.is_union() => false, ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => { let val = cx.tcx.destructure_mir_constant(cx.param_env, val); val.fields.iter().any(|field| inner(cx, *field)) diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index a7cd1f6d0652..9f6917c146f6 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -112,10 +112,7 @@ impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> { self.cx, MANY_SINGLE_CHAR_NAMES, span, - &format!( - "{} bindings with single-character names in scope", - num_single_char_names - ), + &format!("{num_single_char_names} bindings with single-character names in scope"), ); } } diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 25fb4f0f4cff..1a765b14892f 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use clippy_utils::ty::match_type; +use clippy_utils::ty::{is_type_diagnostic_item, match_type}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; 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_span::sym; declare_clippy_lint! { /// ### What it does @@ -49,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { if_chain! { if (path.ident.name == sym!(mode) && (match_type(cx, obj_ty, &paths::OPEN_OPTIONS) - || match_type(cx, obj_ty, &paths::DIR_BUILDER))) + || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder))) || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS)); if let ExprKind::Lit(_) = param.kind; diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index f86dfb6b8df8..0ca0befc1351 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -3,16 +3,17 @@ use std::{ hash::{Hash, Hasher}, }; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; use serde::{de, Deserialize}; declare_clippy_lint! { @@ -39,8 +40,8 @@ declare_clippy_lint! { const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")]; -/// The (name, (open brace, close brace), source snippet) -type MacroInfo<'a> = (Symbol, &'a (String, String), String); +/// The (callsite span, (open brace, close brace), source snippet) +type MacroInfo<'a> = (Span, &'a (String, String), String); #[derive(Clone, Debug, Default)] pub struct MacroBraces { @@ -62,33 +63,29 @@ impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]); impl EarlyLintPass for MacroBraces { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let Some((name, braces, snip)) = is_offending_macro(cx, item.span, self) { - let span = item.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, item.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { - if let Some((name, braces, snip)) = is_offending_macro(cx, stmt.span, self) { - let span = stmt.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, stmt.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if let Some((name, braces, snip)) = is_offending_macro(cx, expr.span, self) { - let span = expr.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, expr.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { - if let Some((name, braces, snip)) = is_offending_macro(cx, ty.span, self) { - let span = ty.span.ctxt().outer_expn_data().call_site; - emit_help(cx, snip, braces, name, span); + if let Some((span, braces, snip)) = is_offending_macro(cx, ty.span, self) { + emit_help(cx, &snip, braces, span); self.done.insert(span); } } @@ -102,48 +99,44 @@ fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a Mac .last() .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local)) }; + let span_call_site = span.ctxt().outer_expn_data().call_site; if_chain! { if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind; let name = mac_name.as_str(); if let Some(braces) = mac_braces.macro_braces.get(name); - if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site); + if let Some(snip) = snippet_opt(cx, span_call_site); // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 - if snip.starts_with(&format!("{}!", name)); + if snip.starts_with(&format!("{name}!")); if unnested_or_local(); // make formatting consistent let c = snip.replace(' ', ""); - if !c.starts_with(&format!("{}!{}", name, braces.0)); - if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site); + if !c.starts_with(&format!("{name}!{}", braces.0)); + if !mac_braces.done.contains(&span_call_site); then { - Some((mac_name, braces, snip)) + Some((span_call_site, braces, snip)) } else { None } } } -fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: Symbol, span: Span) { - let with_space = &format!("! {}", braces.0); - let without_space = &format!("!{}", braces.0); - let mut help = snip; - for b in BRACES.iter().filter(|b| b.0 != braces.0) { - help = help.replace(b.0, &braces.0).replace(b.1, &braces.1); - // Only `{` traditionally has space before the brace - if braces.0 != "{" && help.contains(with_space) { - help = help.replace(with_space, without_space); - } else if braces.0 == "{" && help.contains(without_space) { - help = help.replace(without_space, with_space); - } +fn emit_help(cx: &EarlyContext<'_>, snip: &str, braces: &(String, String), span: Span) { + if let Some((macro_name, macro_args_str)) = snip.split_once('!') { + let mut macro_args = macro_args_str.trim().to_string(); + // now remove the wrong braces + macro_args.remove(0); + macro_args.pop(); + span_lint_and_sugg( + cx, + NONSTANDARD_MACRO_BRACES, + span, + &format!("use of irregular braces for `{macro_name}!` macro"), + "consider writing", + format!("{macro_name}!{}{macro_args}{}", braces.0, braces.1), + Applicability::MachineApplicable, + ); } - span_lint_and_help( - cx, - NONSTANDARD_MACRO_BRACES, - span, - &format!("use of irregular braces for `{}!` macro", name), - Some(span), - &format!("consider writing `{}`", help), - ); } fn macro_braces(conf: FxHashSet) -> FxHashMap { @@ -273,9 +266,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { .iter() .find(|b| b.0 == brace) .map(|(o, c)| ((*o).to_owned(), (*c).to_owned())) - .ok_or_else(|| { - de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{}`", brace)) - })?, + .ok_or_else(|| de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, }) } } diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index bffbf20b4d28..f380a5065827 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -102,7 +102,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { // construct a replacement escape // the maximum value is \077, or \x3f, so u8 is sufficient here if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) { - write!(suggest_1, "\\x{:02x}", n).unwrap(); + write!(suggest_1, "\\x{n:02x}").unwrap(); } // append the null byte as \x00 and the following digits literally diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs index 1ec4240afefe..d29ca37eaeb8 100644 --- a/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -34,13 +34,12 @@ pub(super) fn check<'tcx>( }; let help = format!( - "because `{}` is the {} value for this type, {}", + "because `{}` is the {} value for this type, {conclusion}", snippet(cx, culprit.expr.span, "x"), match culprit.which { ExtremeType::Minimum => "minimum", ExtremeType::Maximum => "maximum", - }, - conclusion + } ); span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help); diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index d24c57c0a4b8..8827daaa3ee7 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,8 +1,3 @@ -#![allow( - // False positive - clippy::match_same_arms -)] - use super::ARITHMETIC_SIDE_EFFECTS; use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; use rustc_ast as ast; @@ -14,11 +9,12 @@ use rustc_session::impl_lint_pass; use rustc_span::source_map::{Span, Spanned}; const HARD_CODED_ALLOWED: &[&str] = &[ + "&str", "f32", "f64", "std::num::Saturating", - "std::string::String", "std::num::Wrapping", + "std::string::String", ]; #[derive(Debug)] @@ -45,61 +41,59 @@ impl ArithmeticSideEffects { /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a /// non-constant environment that won't overflow. fn has_valid_op(op: &Spanned, expr: &hir::Expr<'_>) -> bool { - if let hir::BinOpKind::Add | hir::BinOpKind::Sub = op.node - && let hir::ExprKind::Lit(ref lit) = expr.kind - && let ast::LitKind::Int(0, _) = lit.node - { - return true; - } - if let hir::BinOpKind::Div | hir::BinOpKind::Rem = op.node - && let hir::ExprKind::Lit(ref lit) = expr.kind - && !matches!(lit.node, ast::LitKind::Int(0, _)) + if let hir::ExprKind::Lit(ref lit) = expr.kind && + let ast::LitKind::Int(value, _) = lit.node { - return true; - } - if let hir::BinOpKind::Mul = op.node - && let hir::ExprKind::Lit(ref lit) = expr.kind - && let ast::LitKind::Int(0 | 1, _) = lit.node - { - return true; + match (&op.node, value) { + (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) + | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) + | (hir::BinOpKind::Mul, 0 | 1) => true, + _ => false, + } + } else { + false } - false } /// Checks if the given `expr` has any of the inner `allowed` elements. - fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - self.allowed.contains( - cx.typeck_results() - .expr_ty(expr) - .to_string() - .split('<') - .next() - .unwrap_or_default(), - ) + fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { + self.allowed + .contains(ty.to_string().split('<').next().unwrap_or_default()) } - /// Explicit integers like `1` or `i32::MAX`. Does not take into consideration references. - fn is_literal_integer(expr: &hir::Expr<'_>, expr_refs: Ty<'_>) -> bool { - let is_integral = expr_refs.is_integral(); - let is_literal = matches!(expr.kind, hir::ExprKind::Lit(_)); - is_integral && is_literal + // For example, 8i32 or &i64::MAX. + fn is_integral(ty: Ty<'_>) -> bool { + ty.peel_refs().is_integral() } + // Common entry-point to avoid code duplication. fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { let msg = "arithmetic operation that can potentially result in unexpected side-effects"; span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg); self.expr_span = Some(expr.span); } + /// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`. + fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option> { + if matches!(expr.kind, hir::ExprKind::Lit(_)) { + return Some(LiteralIntegerTy::Value(expr)); + } + if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind { + return Some(LiteralIntegerTy::Ref(inn)); + } + None + } + /// Manages when the lint should be triggered. Operations in constant environments, hard coded /// types, custom allowed types and non-constant operations that won't overflow are ignored. - fn manage_bin_ops( + fn manage_bin_ops<'tcx>( &mut self, - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, + cx: &LateContext<'tcx>, + expr: &hir::Expr<'tcx>, op: &Spanned, - lhs: &hir::Expr<'_>, - rhs: &hir::Expr<'_>, + lhs: &hir::Expr<'tcx>, + rhs: &hir::Expr<'tcx>, ) { if constant_simple(cx, cx.typeck_results(), expr).is_some() { return; @@ -116,17 +110,20 @@ impl ArithmeticSideEffects { ) { return; }; - if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) { + let lhs_ty = cx.typeck_results().expr_ty(lhs); + let rhs_ty = cx.typeck_results().expr_ty(rhs); + let lhs_and_rhs_have_the_same_ty = lhs_ty == rhs_ty; + if lhs_and_rhs_have_the_same_ty && self.is_allowed_ty(lhs_ty) && self.is_allowed_ty(rhs_ty) { return; } - let has_valid_op = match ( - Self::is_literal_integer(lhs, cx.typeck_results().expr_ty(lhs).peel_refs()), - Self::is_literal_integer(rhs, cx.typeck_results().expr_ty(rhs).peel_refs()), - ) { - (true, true) => true, - (true, false) => Self::has_valid_op(op, lhs), - (false, true) => Self::has_valid_op(op, rhs), - (false, false) => false, + let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { + match (Self::literal_integer(lhs), Self::literal_integer(rhs)) { + (None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()), + (Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true, + (None, None) | (Some(_), Some(_)) => false, + } + } else { + false }; if !has_valid_op { self.issue_lint(cx, expr); @@ -135,7 +132,7 @@ impl ArithmeticSideEffects { } impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) { return; } @@ -180,3 +177,22 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { } } } + +/// Tells if an expression is a integer declared by value or by reference. +/// +/// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather +/// than `hirExprKind::Addr`. +enum LiteralIntegerTy<'expr, 'tcx> { + /// For example, `&199` + Ref(&'expr hir::Expr<'tcx>), + /// For example, `1` or `i32::MAX` + Value(&'expr hir::Expr<'tcx>), +} + +impl<'expr, 'tcx> From> for &'expr hir::Expr<'tcx> { + fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self { + match from { + LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem, + } + } +} diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index f134c6c4cdba..26bca7c306a8 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -2,16 +2,17 @@ use clippy_utils::binop_traits; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{eq_expr_value, trait_ref_of_method}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::BorrowKind; use rustc_trait_selection::infer::TyCtxtInferExt; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use super::ASSIGN_OP_PATTERN; @@ -55,7 +56,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( expr.span, "replace it with", - format!("{} {}= {}", snip_a, op.node.as_str(), snip_r), + format!("{snip_a} {}= {snip_r}", op.node.as_str()), Applicability::MachineApplicable, ); } @@ -65,15 +66,19 @@ pub(super) fn check<'tcx>( } }; - let mut visitor = ExprVisitor { - assignee, - counter: 0, - cx, - }; - - walk_expr(&mut visitor, e); + let mut found = false; + let found_multiple = for_each_expr(e, |e| { + if eq_expr_value(cx, assignee, e) { + if found { + return ControlFlow::Break(()); + } + found = true; + } + ControlFlow::Continue(()) + }) + .is_some(); - if visitor.counter == 1 { + if found && !found_multiple { // a = a op b if eq_expr_value(cx, assignee, l) { lint(assignee, r); @@ -98,22 +103,6 @@ pub(super) fn check<'tcx>( } } -struct ExprVisitor<'a, 'tcx> { - assignee: &'a hir::Expr<'a>, - counter: u8, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if eq_expr_value(self.cx, self.assignee, expr) { - self.counter += 1; - } - - walk_expr(self, expr); - } -} - fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet { struct S(hir::HirIdSet); impl Delegate<'_> for S { diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index 74387fbc87be..1369b3e74625 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -64,10 +64,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` can never be equal to `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` can never be equal to `{cmp_value}`"), ); } } else if mask_value == 0 { @@ -80,10 +77,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` can never be equal to `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"), ); } }, @@ -96,10 +90,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` will always be lower than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` will always be lower than `{cmp_value}`"), ); } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); @@ -111,10 +102,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` will never be lower than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` will never be lower than `{cmp_value}`"), ); } else { check_ineffective_lt(cx, span, mask_value, cmp_value, "|"); @@ -130,10 +118,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` will never be higher than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` will never be higher than `{cmp_value}`"), ); } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); @@ -145,10 +130,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` will always be higher than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` will always be higher than `{cmp_value}`"), ); } else { check_ineffective_gt(cx, span, mask_value, cmp_value, "|"); @@ -167,10 +149,7 @@ fn check_ineffective_lt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: cx, INEFFECTIVE_BIT_MASK, span, - &format!( - "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly", - op, m, c - ), + &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), ); } } @@ -181,10 +160,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: cx, INEFFECTIVE_BIT_MASK, span, - &format!( - "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly", - op, m, c - ), + &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), ); } } diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index 638a514ff9b3..c9c777f1bd8d 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -99,7 +99,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) let expr_snip; let eq_impl; if with_deref.is_implemented() { - expr_snip = format!("*{}", arg_snip); + expr_snip = format!("*{arg_snip}"); eq_impl = with_deref; } else { expr_snip = arg_snip.to_string(); @@ -121,17 +121,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) }; if eq_impl.ty_eq_other { hint = format!( - "{}{}{}", - expr_snip, + "{expr_snip}{}{}", snippet(cx, cmp_span, ".."), snippet(cx, other.span, "..") ); } else { hint = format!( - "{}{}{}", + "{}{}{expr_snip}", snippet(cx, other.span, ".."), - snippet(cx, cmp_span, ".."), - expr_snip + snippet(cx, cmp_span, "..") ); } } diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs index 827a2b267093..49e662cacb0c 100644 --- a/clippy_lints/src/operators/duration_subsec.rs +++ b/clippy_lints/src/operators/duration_subsec.rs @@ -31,12 +31,11 @@ pub(crate) fn check<'tcx>( cx, DURATION_SUBSEC, expr.span, - &format!("calling `{}()` is more concise than this calculation", suggested_fn), + &format!("calling `{suggested_fn}()` is more concise than this calculation"), "try", format!( - "{}.{}()", - snippet_with_applicability(cx, self_arg.span, "_", &mut applicability), - suggested_fn + "{}.{suggested_fn}()", + snippet_with_applicability(cx, self_arg.span, "_", &mut applicability) ), applicability, ); diff --git a/clippy_lints/src/operators/eq_op.rs b/clippy_lints/src/operators/eq_op.rs index 44cf0bb06120..67913f7392c0 100644 --- a/clippy_lints/src/operators/eq_op.rs +++ b/clippy_lints/src/operators/eq_op.rs @@ -22,7 +22,7 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { cx, EQ_OP, lhs.span.to(rhs.span), - &format!("identical args used in this `{}!` macro call", macro_name), + &format!("identical args used in this `{macro_name}!` macro call"), ); } } diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index 0024384d9278..ae805147f07a 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -47,18 +47,14 @@ fn lint_misrefactored_assign_op( if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) { let a = &sugg::Sugg::hir(cx, assignee, ".."); let r = &sugg::Sugg::hir(cx, rhs, ".."); - let long = format!("{} = {}", snip_a, sugg::make_binop(op.into(), a, r)); + let long = format!("{snip_a} = {}", sugg::make_binop(op.into(), a, r)); diag.span_suggestion( expr.span, &format!( - "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with", - snip_a, - snip_a, - op.as_str(), - snip_r, - long + "did you mean `{snip_a} = {snip_a} {} {snip_r}` or `{long}`? Consider replacing it with", + op.as_str() ), - format!("{} {}= {}", snip_a, op.as_str(), snip_r), + format!("{snip_a} {}= {snip_r}", op.as_str()), Applicability::MaybeIncorrect, ); diag.span_suggestion( diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index c32b4df4f75c..b8a20d5ebe9b 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -67,7 +67,7 @@ declare_clippy_lint! { /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), /// or can panic (`/`, `%`). /// - /// Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant + /// Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant /// environments, allowed types and non-constant operations that won't overflow are ignored. /// /// ### Why is this bad? diff --git a/clippy_lints/src/operators/needless_bitwise_bool.rs b/clippy_lints/src/operators/needless_bitwise_bool.rs index e902235a014e..ab5fb1787004 100644 --- a/clippy_lints/src/operators/needless_bitwise_bool.rs +++ b/clippy_lints/src/operators/needless_bitwise_bool.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Exp if let Some(lhs_snip) = snippet_opt(cx, lhs.span) && let Some(rhs_snip) = snippet_opt(cx, rhs.span) { - let sugg = format!("{} {} {}", lhs_snip, op_str, rhs_snip); + let sugg = format!("{lhs_snip} {op_str} {rhs_snip}"); diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs index b6097710dc68..0830a106f556 100644 --- a/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/clippy_lints/src/operators/numeric_arithmetic.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::constant_simple; use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_integer_literal; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; @@ -50,11 +51,9 @@ impl Context { hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { hir::ExprKind::Lit(_lit) => (), hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { - if let hir::ExprKind::Lit(lit) = &expr.kind { - if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - } + if is_integer_literal(expr, 1) { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_id = Some(expr.hir_id); } }, _ => { diff --git a/clippy_lints/src/operators/ptr_eq.rs b/clippy_lints/src/operators/ptr_eq.rs index 1aefc2741c21..1229c202f5a0 100644 --- a/clippy_lints/src/operators/ptr_eq.rs +++ b/clippy_lints/src/operators/ptr_eq.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( expr.span, LINT_MSG, "try", - format!("std::ptr::eq({}, {})", left_snip, right_snip), + format!("std::ptr::eq({left_snip}, {right_snip})"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/operators/self_assignment.rs b/clippy_lints/src/operators/self_assignment.rs index 9d6bec05bf09..7c9d5320a3a8 100644 --- a/clippy_lints/src/operators/self_assignment.rs +++ b/clippy_lints/src/operators/self_assignment.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, lhs: &'tcx cx, SELF_ASSIGNMENT, e.span, - &format!("self-assignment of `{}` to `{}`", rhs, lhs), + &format!("self-assignment of `{rhs}` to `{lhs}`"), ); } } diff --git a/clippy_lints/src/operators/verbose_bit_mask.rs b/clippy_lints/src/operators/verbose_bit_mask.rs index ff85fd554298..fbf65e92b322 100644 --- a/clippy_lints/src/operators/verbose_bit_mask.rs +++ b/clippy_lints/src/operators/verbose_bit_mask.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "try", - format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()), + format!("{sugg}.trailing_zeros() >= {}", n.count_ones()), Applicability::MaybeIncorrect, ); }, diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 0315678bf97a..4eb42da1fed0 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::{ - can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks, + can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_res_lang_ctor, peel_blocks, peel_hir_expr_while, CaptureKind, }; use if_chain::if_chain; @@ -88,7 +88,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// None/_ => {..} /// } /// ``` -struct OptionOccurence { +struct OptionOccurrence { option: String, method_sugg: String, some_expr: String, @@ -109,13 +109,13 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo ) } -fn try_get_option_occurence<'tcx>( +fn try_get_option_occurrence<'tcx>( cx: &LateContext<'tcx>, pat: &Pat<'tcx>, expr: &Expr<'_>, if_then: &'tcx Expr<'_>, if_else: &'tcx Expr<'_>, -) -> Option { +) -> Option { let cond_expr = match expr.kind { ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr, _ => expr, @@ -160,10 +160,10 @@ fn try_get_option_occurence<'tcx>( } } - return Some(OptionOccurence { + return Some(OptionOccurrence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), - some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")), + some_expr: format!("|{capture_mut}{capture_name}| {}", Sugg::hir_with_macro_callsite(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")), }); } @@ -174,7 +174,8 @@ fn try_get_option_occurence<'tcx>( fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> { if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk) { + let res = cx.qpath_res(qpath, pat.hir_id); + if is_res_lang_ctor(cx, res, OptionSome) || is_res_lang_ctor(cx, res, ResultOk) { return Some(inner_pat); } } @@ -182,9 +183,9 @@ fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&' } /// If this expression is the option if let/else construct we're detecting, then -/// this function returns an `OptionOccurence` struct with details if +/// this function returns an `OptionOccurrence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { +fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if let Some(higher::IfLet { let_pat, let_expr, @@ -193,16 +194,16 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> }) = higher::IfLet::hir(cx, expr) { if !is_else_clause(cx.tcx, expr) { - return try_get_option_occurence(cx, let_pat, let_expr, if_then, if_else); + return try_get_option_occurrence(cx, let_pat, let_expr, if_then, if_else); } } None } -fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { +fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind { if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) { - return try_get_option_occurence(cx, let_pat, ex, if_then, if_else); + return try_get_option_occurrence(cx, let_pat, ex, if_then, if_else); } } None @@ -226,9 +227,10 @@ fn try_convert_match<'tcx>( fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { match arm.pat.kind { - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [first_pat], _) => { - is_lang_ctor(cx, qpath, ResultErr) && matches!(first_pat.kind, PatKind::Wild) + is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) + && matches!(first_pat.kind, PatKind::Wild) }, PatKind::Wild => true, _ => false, diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 4aa0d9227aba..efec12489a9b 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -2,9 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::return_ty; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::expr_visitor_no_bodies; +use clippy_utils::visitors::{for_each_expr, Descend}; +use core::ops::ControlFlow; use rustc_hir as hir; -use rustc_hir::intravisit::{FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -58,18 +59,20 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let mut panics = Vec::new(); - expr_visitor_no_bodies(|expr| { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true }; + let _: Option = for_each_expr(body.value, |e| { + let Some(macro_call) = root_macro_call_first_node(cx, e) else { + return ControlFlow::Continue(Descend::Yes); + }; if matches!( cx.tcx.item_name(macro_call.def_id).as_str(), "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" ) { panics.push(macro_call.span); - return false; + ControlFlow::Continue(Descend::No) + } else { + ControlFlow::Continue(Descend::Yes) } - true - }) - .visit_expr(body.value); + }); if !panics.is_empty() { span_lint_and_then( cx, diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 000b0ba7a148..6810a2431758 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -1,5 +1,5 @@ use clippy_utils::{ - diagnostics::span_lint_and_sugg, is_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg, + diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg, ty::is_type_diagnostic_item, }; use rustc_errors::Applicability; @@ -54,8 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { !expr.span.from_expansion() - && matches!(&peel_hir_expr_refs(expr).0.kind, - ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone)) + && is_res_lang_ctor(cx, path_res(cx, peel_hir_expr_refs(expr).0), LangItem::OptionNone) }; let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 6b2eea489322..45e98de10ace 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -209,7 +209,7 @@ impl<'tcx> PassByRefOrValue { cx, TRIVIALLY_COPY_PASS_BY_REF, input.span, - &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size), + &format!("this argument ({size} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", self.ref_min_size), "consider passing by value instead", value_type, Applicability::Unspecified, @@ -237,7 +237,7 @@ impl<'tcx> PassByRefOrValue { cx, LARGE_TYPES_PASSED_BY_VALUE, input.span, - &format!("this argument ({} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", size, self.value_max_size), + &format!("this argument ({size} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", self.value_max_size), "consider passing by reference instead", format!("&{}", snippet(cx, input.span, "_")), Applicability::MaybeIncorrect, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 41d1baba64f8..d296a150b46d 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -463,7 +463,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( diag.span_suggestion( hir_ty.span, "change this to", - format!("&{}{}", mutability.prefix_str(), ty_name), + format!("&{}{ty_name}", mutability.prefix_str()), Applicability::Unspecified, ); } @@ -669,7 +669,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { - if let TyKind::Rptr(ref lt, ref m) = ty.kind { + if let TyKind::Rptr(lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) } else { None diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 4dc65da3ea1f..b0a5d1a67582 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { None => return, }; - let msg = format!("use of `{}` with a `usize` casted to an `isize`", method); + let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) { span_lint_and_sugg( cx, @@ -124,7 +124,7 @@ fn build_suggestion<'tcx>( ) -> Option { let receiver = snippet_opt(cx, receiver_expr.span)?; let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?; - Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs)) + Some(format!("{receiver}.{}({cast_lhs})", method.suggestion())) } #[derive(Copy, Clone)] diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 569870ab2b7f..328371fd602f 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -3,11 +3,12 @@ use clippy_utils::higher; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ - eq_expr_value, get_parent_node, is_else_clause, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, + eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, path_to_local, path_to_local_id, + peel_blocks, peel_blocks_with_stmt, }; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, Node, PatKind, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -58,7 +59,7 @@ enum IfBlockType<'hir> { /// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c), /// if_else (d) IfLet( - &'hir QPath<'hir>, + Res, Ty<'hir>, Symbol, &'hir Expr<'hir>, @@ -97,12 +98,12 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); let sugg = if let Some(else_inner) = r#else { if eq_expr_value(cx, caller, peel_blocks(else_inner)) { - format!("Some({}?)", receiver_str) + format!("Some({receiver_str}?)") } else { return; } } else { - format!("{}{}?;", receiver_str, if by_ref { ".as_ref()" } else { "" }) + format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" }) }; span_lint_and_sugg( @@ -126,7 +127,14 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if ddpos.as_opt_usize().is_none(); if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind; let caller_ty = cx.typeck_results().expr_ty(let_expr); - let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else); + let if_block = IfBlockType::IfLet( + cx.qpath_res(path1, let_pat.hir_id), + caller_ty, + ident.name, + let_expr, + if_then, + if_else + ); if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) || is_early_return(sym::Result, cx, &if_block); if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none(); @@ -135,8 +143,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_))); let sugg = format!( - "{}{}?{}", - receiver_str, + "{receiver_str}{}?{}", if by_ref == ByRef::Yes { ".as_ref()" } else { "" }, if requires_semi { ";" } else { "" } ); @@ -166,21 +173,21 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ _ => false, } }, - IfBlockType::IfLet(qpath, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => { + IfBlockType::IfLet(res, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => { is_type_diagnostic_item(cx, let_expr_ty, smbl) && match smbl { sym::Option => { // We only need to check `if let Some(x) = option` not `if let None = option`, // because the later one will be suggested as `if option.is_none()` thus causing conflict. - is_lang_ctor(cx, qpath, OptionSome) + is_res_lang_ctor(cx, res, OptionSome) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None) }, sym::Result => { - (is_lang_ctor(cx, qpath, ResultOk) + (is_res_lang_ctor(cx, res, ResultOk) && if_else.is_some() && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) - || is_lang_ctor(cx, qpath, ResultErr) + || is_res_lang_ctor(cx, res, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) }, _ => false, @@ -199,7 +206,7 @@ fn expr_return_none_or_err( match peel_blocks_with_stmt(expr).kind { ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym), ExprKind::Path(ref qpath) => match smbl { - sym::Option => is_lang_ctor(cx, qpath, OptionNone), + sym::Option => is_res_lang_ctor(cx, cx.qpath_res(qpath, expr.hir_id), OptionNone), sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr), _ => false, }, @@ -224,7 +231,9 @@ fn expr_return_none_or_err( impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - check_is_none_or_err_and_early_return(cx, expr); - check_if_let_some_or_err_and_early_return(cx, expr); + if !in_constant(cx, expr.hir_id) { + check_is_none_or_err_and_early_return(cx, expr); + check_if_let_some_or_err_and_early_return(cx, expr); + } } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 918d624eec6f..c6fbb5e805ab 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -243,9 +243,9 @@ fn check_possible_range_contains( cx, MANUAL_RANGE_CONTAINS, span, - &format!("manual `{}::contains` implementation", range_type), + &format!("manual `{range_type}::contains` implementation"), "use", - format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), + format!("({lo}{space}{range_op}{hi}).contains(&{name})"), applicability, ); } else if !combine_and && ord == Some(l.ord) { @@ -273,9 +273,9 @@ fn check_possible_range_contains( cx, MANUAL_RANGE_CONTAINS, span, - &format!("manual `!{}::contains` implementation", range_type), + &format!("manual `!{range_type}::contains` implementation"), "use", - format!("!({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), + format!("!({lo}{space}{range_op}{hi}).contains(&{name})"), applicability, ); } @@ -372,14 +372,14 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { diag.span_suggestion( span, "use", - format!("({}..={})", start, end), + format!("({start}..={end})"), Applicability::MaybeIncorrect, ); } else { diag.span_suggestion( span, "use", - format!("{}..={}", start, end), + format!("{start}..={end}"), Applicability::MachineApplicable, // snippet ); } @@ -408,7 +408,7 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { diag.span_suggestion( expr.span, "use", - format!("{}..{}", start, end), + format!("{start}..{end}"), Applicability::MachineApplicable, // snippet ); }, @@ -486,7 +486,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "consider using the following if you are attempting to iterate over this \ range in reverse", - format!("({}{}{}).rev()", end_snippet, dots, start_snippet), + format!("({end_snippet}{dots}{start_snippet}).rev()"), Applicability::MaybeIncorrect, ); } diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index 94dec191103c..fa107858863a 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -2,9 +2,10 @@ use clippy_utils::{ diagnostics::{span_lint, span_lint_and_sugg}, higher::{get_vec_init_kind, VecInitKind}, source::snippet, - visitors::expr_visitor_no_bodies, + visitors::for_each_expr, }; -use hir::{intravisit::Visitor, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; +use core::ops::ControlFlow; +use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -58,10 +59,8 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { && let PatKind::Binding(_, _, ident, _) = pat.kind && let Some(vec_init_kind) = get_vec_init_kind(cx, init) { - // finds use of `_.read(&mut v)` - let mut read_found = false; - let mut visitor = expr_visitor_no_bodies(|expr| { - if let ExprKind::MethodCall(path, _self, [arg], _) = expr.kind + let visitor = |expr: &Expr<'_>| { + if let ExprKind::MethodCall(path, _, [arg], _) = expr.kind && let PathSegment { ident: read_or_read_exact, .. } = *path && matches!(read_or_read_exact.as_str(), "read" | "read_exact") && let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind @@ -69,27 +68,22 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { && let [inner_seg] = inner_path.segments && ident.name == inner_seg.ident.name { - read_found = true; + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - !read_found - }); + }; - let next_stmt_span; - if idx == block.stmts.len() - 1 { + let (read_found, next_stmt_span) = + if let Some(next_stmt) = block.stmts.get(idx + 1) { + // case { .. stmt; stmt; .. } + (for_each_expr(next_stmt, visitor).is_some(), next_stmt.span) + } else if let Some(e) = block.expr { // case { .. stmt; expr } - if let Some(e) = block.expr { - visitor.visit_expr(e); - next_stmt_span = e.span; - } else { - return; - } + (for_each_expr(e, visitor).is_some(), e.span) } else { - // case { .. stmt; stmt; .. } - let next_stmt = &block.stmts[idx + 1]; - visitor.visit_stmt(next_stmt); - next_stmt_span = next_stmt.span; - } - drop(visitor); + return + }; if read_found && !next_stmt_span.from_expansion() { let applicability = Applicability::MaybeIncorrect; @@ -101,9 +95,8 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { next_stmt_span, "reading zero byte data to `Vec`", "try", - format!("{}.resize({}, 0); {}", + format!("{}.resize({len}, 0); {}", ident.as_str(), - len, snippet(cx, next_stmt_span, "..") ), applicability, diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 3c6ca9d98975..464f6827e1d5 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { cx, REDUNDANT_PUB_CRATE, span, - &format!("pub(crate) {} inside private module", descr), + &format!("pub(crate) {descr} inside private module"), |diag| { diag.span_suggestion( item.vis_span, diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 8693ca9af830..245a02ea26e6 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -127,9 +127,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix { - format!("({}{}{})", reborrow_str, "*".repeat(deref_count), snip) + format!("({reborrow_str}{}{snip})", "*".repeat(deref_count)) } else { - format!("{}{}{}", reborrow_str, "*".repeat(deref_count), snip) + format!("{reborrow_str}{}{snip}", "*".repeat(deref_count)) }; (lint, help_str, sugg) @@ -141,9 +141,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { if deref_ty == expr_ty { let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; let sugg = if needs_parens_for_prefix { - format!("(&{}{}*{})", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip) + format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count)) } else { - format!("&{}{}*{}", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip) + format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count)) }; (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg) } else { diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 2d751c274679..60ba62c4a433 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -67,7 +67,7 @@ impl RedundantStaticLifetimes { TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime { let snip = snippet(cx, borrow_type.ty.span, ""); - let sugg = format!("&{}", snip); + let sugg = format!("&{snip}"); span_lint_and_then( cx, REDUNDANT_STATIC_LIFETIMES, diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 6bcae0da8f48..1fda58fa54de 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -172,7 +172,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { ); }, Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e)); + span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); }, } } @@ -200,7 +200,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { ); }, Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {}", e)); + span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); }, } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 91553240e3c9..2b2a41d16011 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,9 +1,11 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; +use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::{fn_def_id, path_to_local_id}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::FnKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -72,6 +74,27 @@ enum RetReplacement { Unit, } +impl RetReplacement { + fn sugg_help(self) -> &'static str { + match self { + Self::Empty => "remove `return`", + Self::Block => "replace `return` with an empty block", + Self::Unit => "replace `return` with a unit value", + } + } +} + +impl ToString for RetReplacement { + fn to_string(&self) -> String { + match *self { + Self::Empty => "", + Self::Block => "{}", + Self::Unit => "()", + } + .to_string() + } +} + declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); impl<'tcx> LateLintPass<'tcx> for Return { @@ -139,26 +162,35 @@ impl<'tcx> LateLintPass<'tcx> for Return { } else { RetReplacement::Empty }; - check_final_expr(cx, body.value, Some(body.value.span), replacement); + check_final_expr(cx, body.value, vec![], replacement); }, FnKind::ItemFn(..) | FnKind::Method(..) => { - if let ExprKind::Block(block, _) = body.value.kind { - check_block_return(cx, block); - } + check_block_return(cx, &body.value.kind, vec![]); }, } } } -fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { - if let Some(expr) = block.expr { - check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(expr) | StmtKind::Semi(expr) => { - check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - }, - _ => (), +// if `expr` is a block, check if there are needless returns in it +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, semi_spans: Vec) { + if let ExprKind::Block(block, _) = expr_kind { + if let Some(block_expr) = block.expr { + check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty); + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(expr) => { + check_final_expr(cx, expr, semi_spans, RetReplacement::Empty); + }, + StmtKind::Semi(semi_expr) => { + let mut semi_spans_and_this_one = semi_spans; + // we only want the span containing the semicolon so we can remove it later. From `entry.rs:382` + if let Some(semicolon_span) = stmt.span.trim_start(semi_expr.span) { + semi_spans_and_this_one.push(semicolon_span); + check_final_expr(cx, semi_expr, semi_spans_and_this_one, RetReplacement::Empty); + } + }, + _ => (), + } } } } @@ -166,10 +198,12 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { fn check_final_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, - span: Option, + semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an + * needless return */ replacement: RetReplacement, ) { - match expr.kind { + let peeled_drop_expr = expr.peel_drop_temps(); + match &peeled_drop_expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { if cx.tcx.hir().attrs(expr.hir_id).is_empty() { @@ -177,24 +211,18 @@ fn check_final_expr<'tcx>( if !borrows { emit_return_lint( cx, - inner.map_or(expr.hir_id, |inner| inner.hir_id), - span.expect("`else return` is not possible"), + peeled_drop_expr.span, + semi_spans, inner.as_ref().map(|i| i.span), replacement, ); } } }, - // a whole block? check it! - ExprKind::Block(block, _) => { - check_block_return(cx, block); - }, ExprKind::If(_, then, else_clause_opt) => { - if let ExprKind::Block(ifblock, _) = then.kind { - check_block_return(cx, ifblock); - } + check_block_return(cx, &then.kind, semi_spans.clone()); if let Some(else_clause) = else_clause_opt { - check_final_expr(cx, else_clause, None, RetReplacement::Empty); + check_block_return(cx, &else_clause.kind, semi_spans); } }, // a match expr, check all arms @@ -203,123 +231,61 @@ fn check_final_expr<'tcx>( // (except for unit type functions) so we don't match it ExprKind::Match(_, arms, MatchSource::Normal) => { for arm in arms.iter() { - check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Unit); + check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit); } }, - ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty), - _ => (), + // if it's a whole block, check it + other_expr_kind => check_block_return(cx, other_expr_kind, semi_spans), } } fn emit_return_lint( cx: &LateContext<'_>, - emission_place: HirId, ret_span: Span, + semi_spans: Vec, inner_span: Option, replacement: RetReplacement, ) { if ret_span.from_expansion() { return; } - match inner_span { - Some(inner_span) => { - let mut applicability = Applicability::MachineApplicable; - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability); - diag.span_suggestion(ret_span, "remove `return`", snippet, applicability); - }, - ); - }, - None => match replacement { - RetReplacement::Empty => { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - diag.span_suggestion( - ret_span, - "remove `return`", - String::new(), - Applicability::MachineApplicable, - ); - }, - ); - }, - RetReplacement::Block => { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - diag.span_suggestion( - ret_span, - "replace `return` with an empty block", - "{}".to_string(), - Applicability::MachineApplicable, - ); - }, - ); - }, - RetReplacement::Unit => { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - emission_place, - ret_span, - "unneeded `return` statement", - |diag| { - diag.span_suggestion( - ret_span, - "replace `return` with a unit value", - "()".to_string(), - Applicability::MachineApplicable, - ); - }, - ); - }, + let mut applicability = Applicability::MachineApplicable; + let return_replacement = inner_span.map_or_else( + || replacement.to_string(), + |inner_span| { + let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability); + snippet.to_string() }, - } + ); + let sugg_help = if inner_span.is_some() { + "remove `return`" + } else { + replacement.sugg_help() + }; + span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { + diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability); + // for each parent statement, we need to remove the semicolon + for semi_stmt_span in semi_spans { + diag.tool_only_span_suggestion(semi_stmt_span, "remove this semicolon", "", applicability); + } + }); } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows || expr.span.from_expansion() { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx + for_each_expr(expr, |e| { + if let Some(def_id) = fn_def_id(cx, e) + && cx .tcx .fn_sig(def_id) - .output() .skip_binder() + .output() .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::from(!expr.span.from_expansion())) } - - walk_expr(self, expr); - } + }) + .is_some() } diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 20184d54b76e..4249063d2d47 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { |diag| { diag.span_note( trait_method_span, - &format!("existing `{}` defined here", method_name), + &format!("existing `{method_name}` defined here"), ); }, ); @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { // iterate on trait_spans? diag.span_note( trait_spans[0], - &format!("existing `{}` defined here", method_name), + &format!("existing `{method_name}` defined here"), ); }, ); diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 729694da46d5..66638eed9983 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { } let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, ".."); - let suggestion = format!("{0};", sugg); + let suggestion = format!("{sugg};"); span_lint_and_sugg( cx, SEMICOLON_IF_NOTHING_RETURNED, diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index c07aa00a1278..760399231513 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq}; +use clippy_utils::{ + get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, SpanlessEq, +}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind}; @@ -174,7 +175,7 @@ impl SlowVectorInit { diag.span_suggestion( vec_alloc.allocation_expr.span, "consider replace allocation with", - format!("vec![0; {}]", len_expr), + format!("vec![0; {len_expr}]"), Applicability::Unspecified, ); }); @@ -219,8 +220,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { && path_to_local_id(self_arg, self.vec_alloc.local_id) && path.ident.name == sym!(resize) // Check that is filled with 0 - && let ExprKind::Lit(ref lit) = fill_arg.kind - && let LitKind::Int(0, _) = lit.node { + && is_integer_literal(fill_arg, 0) { // Check that len expression is equals to `with_capacity` expression if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) { self.slow_expression = Some(InitializationType::Resize(expr)); @@ -254,10 +254,8 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { if_chain! { if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind; - if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT); - if let ExprKind::Lit(ref lit) = repeat_arg.kind; - if let LitKind::Int(0, _) = lit.node; - + if is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat); + if is_integer_literal(repeat_arg, 0); then { true } else { diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index ffd63cc687a1..d6b336bef943 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::def_id::DefId; use rustc_hir::{def::Res, HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::DefIdTree; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, symbol::kw, Span}; @@ -94,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { if let Res::Def(_, def_id) = path.res && let Some(first_segment) = get_first_segment(path) + && is_stable(cx, def_id) { let (lint, msg, help) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { @@ -146,3 +149,22 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> _ => None, } } + +/// Checks if all ancestors of `def_id` are stable, to avoid linting +/// [unstable moves](https://github.com/rust-lang/rust/pull/95956) +fn is_stable(cx: &LateContext<'_>, mut def_id: DefId) -> bool { + loop { + if cx + .tcx + .lookup_stability(def_id) + .map_or(false, |stability| stability.is_unstable()) + { + return false; + } + + match cx.tcx.opt_parent(def_id) { + Some(parent) => def_id = parent, + None => return true, + } + } +} diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 662d399ca538..d356c99c8fc4 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -284,7 +284,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { e.span, "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", "try", - format!("Some(&{}[{}])", snippet_app, snippet(cx, right.span, "..")), + format!("Some(&{snippet_app}[{}])", snippet(cx, right.span, "..")), applicability ) } @@ -500,8 +500,8 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { cx, TRIM_SPLIT_WHITESPACE, trim_span.with_hi(split_ws_span.lo()), - &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name), - &format!("remove `{}()`", trim_fn_name), + &format!("found call to `str::{trim_fn_name}` before `str::split_whitespace`"), + &format!("remove `{trim_fn_name}()`"), String::new(), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index 78403d9fdb7e..03324c66e8ef 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { span, "using `libc::strlen` on a `CString` or `CStr` value", "try this", - format!("{}.{}().len()", val_name, method_name), + format!("{val_name}.{method_name}().len()"), app, ); } diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 5d36f0f5ff8b..eef9bdc78494 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -326,8 +326,7 @@ fn replace_left_sugg( applicability: &mut Applicability, ) -> String { format!( - "{} {} {}", - left_suggestion, + "{left_suggestion} {} {}", binop.op.to_string(), snippet_with_applicability(cx, binop.right.span, "..", applicability), ) @@ -340,10 +339,9 @@ fn replace_right_sugg( applicability: &mut Applicability, ) -> String { format!( - "{} {} {}", + "{} {} {right_suggestion}", snippet_with_applicability(cx, binop.left.span, "..", applicability), binop.op.to_string(), - right_suggestion, ) } @@ -676,9 +674,8 @@ fn suggestion_with_swapped_ident( } Some(format!( - "{}{}{}", + "{}{new_ident}{}", snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability), - new_ident, snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability), )) }) diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index d47ed459387e..b57b484bdc89 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -92,25 +93,17 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { } fn count_binops(expr: &hir::Expr<'_>) -> u32 { - let mut visitor = BinaryExprVisitor::default(); - visitor.visit_expr(expr); - visitor.nb_binops -} - -#[derive(Default)] -struct BinaryExprVisitor { - nb_binops: u32, -} - -impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - match expr.kind { + let mut count = 0u32; + let _: Option = for_each_expr(expr, |e| { + if matches!( + e.kind, hir::ExprKind::Binary(..) - | hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _) - | hir::ExprKind::AssignOp(..) => self.nb_binops += 1, - _ => {}, + | hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _) + | hir::ExprKind::AssignOp(..) + ) { + count += 1; } - - walk_expr(self, expr); - } + ControlFlow::Continue(()) + }); + count } diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 1885f3ca414d..f46c21e12655 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -96,7 +96,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa cx, MANUAL_SWAP, span, - &format!("this looks like you are swapping elements of `{}` manually", slice), + &format!("this looks like you are swapping elements of `{slice}` manually"), "try", format!( "{}.swap({}, {})", @@ -121,16 +121,16 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa cx, MANUAL_SWAP, span, - &format!("this looks like you are swapping `{}` and `{}` manually", first, second), + &format!("this looks like you are swapping `{first}` and `{second}` manually"), |diag| { diag.span_suggestion( span, "try", - format!("{}::mem::swap({}, {})", sugg, first.mut_addr(), second.mut_addr()), + format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()), applicability, ); if !is_xor_based { - diag.note(&format!("or maybe you should use `{}::mem::replace`?", sugg)); + diag.note(&format!("or maybe you should use `{sugg}::mem::replace`?")); } }, ); @@ -182,7 +182,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { let rhs0 = Sugg::hir_opt(cx, rhs0); let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) { ( - format!(" `{}` and `{}`", first, second), + format!(" `{first}` and `{second}`"), first.mut_addr().to_string(), second.mut_addr().to_string(), ) @@ -196,22 +196,19 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { span_lint_and_then(cx, ALMOST_SWAPPED, span, - &format!("this looks like you are trying to swap{}", what), + &format!("this looks like you are trying to swap{what}"), |diag| { if !what.is_empty() { diag.span_suggestion( span, "try", format!( - "{}::mem::swap({}, {})", - sugg, - lhs, - rhs, + "{sugg}::mem::swap({lhs}, {rhs})", ), Applicability::MaybeIncorrect, ); diag.note( - &format!("or maybe you should use `{}::mem::replace`?", sugg) + &format!("or maybe you should use `{sugg}::mem::replace`?") ); } }); diff --git a/clippy_lints/src/swap_ptr_to_ref.rs b/clippy_lints/src/swap_ptr_to_ref.rs index 3cbbda80f3a9..d085dda3582b 100644 --- a/clippy_lints/src/swap_ptr_to_ref.rs +++ b/clippy_lints/src/swap_ptr_to_ref.rs @@ -58,7 +58,7 @@ impl LateLintPass<'_> for SwapPtrToRef { let mut app = Applicability::MachineApplicable; let snip1 = snippet_with_context(cx, arg1_span.unwrap_or(arg1.span), ctxt, "..", &mut app).0; let snip2 = snippet_with_context(cx, arg2_span.unwrap_or(arg2.span), ctxt, "..", &mut app).0; - diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({}, {})", snip1, snip2), app); + diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({snip1}, {snip2})"), app); } } ); diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs index 651201f34ed2..2512500a6be7 100644 --- a/clippy_lints/src/to_digit_is_some.rs +++ b/clippy_lints/src/to_digit_is_some.rs @@ -84,9 +84,9 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { "use of `.to_digit(..).is_some()`", "try this", if is_method_call { - format!("{}.is_digit({})", char_arg_snip, radix_snip) + format!("{char_arg_snip}.is_digit({radix_snip})") } else { - format!("char::is_digit({}, {})", char_arg_snip, radix_snip) + format!("char::is_digit({char_arg_snip}, {radix_snip})") }, applicability, ); diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 2be22884027e..b5f11b4acae0 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -215,9 +215,8 @@ impl TraitBounds { .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability)) .join(" + "); let hint_string = format!( - "consider combining the bounds: `{}: {}`", + "consider combining the bounds: `{}: {trait_bounds}`", snippet(cx, p.bounded_ty.span, "_"), - trait_bounds, ); span_lint_and_help( cx, diff --git a/clippy_lints/src/transmute/crosspointer_transmute.rs b/clippy_lints/src/transmute/crosspointer_transmute.rs index 25d0543c8611..c4b9d82fc735 100644 --- a/clippy_lints/src/transmute/crosspointer_transmute.rs +++ b/clippy_lints/src/transmute/crosspointer_transmute.rs @@ -13,10 +13,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, CROSSPOINTER_TRANSMUTE, e.span, - &format!( - "transmute from a type (`{}`) to the type that it points to (`{}`)", - from_ty, to_ty - ), + &format!("transmute from a type (`{from_ty}`) to the type that it points to (`{to_ty}`)"), ); true }, @@ -25,10 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, CROSSPOINTER_TRANSMUTE, e.span, - &format!( - "transmute from a type (`{}`) to a pointer to that type (`{}`)", - from_ty, to_ty - ), + &format!("transmute from a type (`{from_ty}`) to a pointer to that type (`{to_ty}`)"), ); true }, diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index 1bde977cfa27..5ecba512b0fd 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_FLOAT_TO_INT, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let mut sugg = sugg::Sugg::hir(cx, arg, ".."); @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( if let ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node; then { - let op = format!("{}{}", sugg, float_ty.name_str()).into(); + let op = format!("{sugg}{}", float_ty.name_str()).into(); match sugg { sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op), _ => sugg = sugg::Sugg::NonParen(op) diff --git a/clippy_lints/src/transmute/transmute_int_to_bool.rs b/clippy_lints/src/transmute/transmute_int_to_bool.rs index 8c50b58ca4b8..58227c53de2f 100644 --- a/clippy_lints/src/transmute/transmute_int_to_bool.rs +++ b/clippy_lints/src/transmute/transmute_int_to_bool.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_BOOL, e.span, - &format!("transmute from a `{}` to a `bool`", from_ty), + &format!("transmute from a `{from_ty}` to a `bool`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let zero = sugg::Sugg::NonParen(Cow::from("0")); diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs index 9e1823c373bf..7d31c375f8cf 100644 --- a/clippy_lints/src/transmute/transmute_int_to_char.rs +++ b/clippy_lints/src/transmute/transmute_int_to_char.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_CHAR, e.span, - &format!("transmute from a `{}` to a `char`", from_ty), + &format!("transmute from a `{from_ty}` to a `char`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let arg = if let ty::Int(_) = from_ty.kind() { @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("std::char::from_u32({}).unwrap()", arg), + format!("std::char::from_u32({arg}).unwrap()"), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_int_to_float.rs b/clippy_lints/src/transmute/transmute_int_to_float.rs index b8703052e6c8..cc3422edbbf1 100644 --- a/clippy_lints/src/transmute/transmute_int_to_float.rs +++ b/clippy_lints/src/transmute/transmute_int_to_float.rs @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_INT_TO_FLOAT, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let arg = if let ty::Int(int_ty) = from_ty.kind() { @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "consider using", - format!("{}::from_bits({})", to_ty, arg), + format!("{to_ty}::from_bits({arg})"), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/clippy_lints/src/transmute/transmute_num_to_bytes.rs index 52d193d11e1a..009d5a7c8ae1 100644 --- a/clippy_lints/src/transmute/transmute_num_to_bytes.rs +++ b/clippy_lints/src/transmute/transmute_num_to_bytes.rs @@ -31,13 +31,13 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_NUM_TO_BYTES, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); diag.span_suggestion( e.span, "consider using `to_ne_bytes()`", - format!("{}.to_ne_bytes()", arg), + format!("{arg}.to_ne_bytes()"), Applicability::Unspecified, ); }, diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 5eb03275b8ec..12d0b866e1c9 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -25,10 +25,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_PTR_TO_REF, e.span, - &format!( - "transmute from a pointer type (`{}`) to a reference type (`{}`)", - from_ty, to_ty - ), + &format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); let (deref, cast) = if *mutbl == Mutability::Mut { @@ -41,26 +38,25 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); if meets_msrv(msrv, msrvs::POINTER_CAST) { - format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), ty_snip) + format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par()) } else if from_ptr_ty.has_erased_regions() { - sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, ty_snip))) - .to_string() + sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() } else { - sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, ty_snip))).to_string() + sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() } } else if from_ptr_ty.ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { if meets_msrv(msrv, msrvs::POINTER_CAST) { - format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), to_ref_ty) + format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) } else { - sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, to_ref_ty))) + sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) .to_string() } } else { sugg::make_unop(deref, arg).to_string() } } else { - sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, to_ref_ty))).to_string() + sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() }; diag.span_suggestion(e.span, "try", sugg, app); diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 707a11d361c0..afb7f2e13269 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_BYTES_TO_STR, e.span, - &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + &format!("transmute from a `{from_ty}` to a `{to_ty}`"), "consider using", if const_context { format!("std::str::from_utf8_unchecked{postfix}({snippet})") diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index ae55a6bf5586..1c99a02e6c71 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -75,10 +75,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty.peel_refs() { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -89,10 +89,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute to `{}` which has an undefined layout", to_ty_orig), + &format!("transmute to `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty.peel_refs() { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } }, ); @@ -116,8 +116,7 @@ pub(super) fn check<'tcx>( TRANSMUTE_UNDEFINED_REPR, e.span, &format!( - "transmute from `{}` to `{}`, both of which have an undefined layout", - from_ty_orig, to_ty_orig + "transmute from `{from_ty_orig}` to `{to_ty_orig}`, both of which have an undefined layout" ), |diag| { if let Some(same_adt_did) = same_adt_did { @@ -127,10 +126,10 @@ pub(super) fn check<'tcx>( )); } else { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } } }, @@ -145,10 +144,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -162,10 +161,10 @@ pub(super) fn check<'tcx>( cx, TRANSMUTE_UNDEFINED_REPR, e.span, - &format!("transmute into `{}` which has an undefined layout", to_ty_orig), + &format!("transmute into `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } }, ); diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 626d7cd46fc4..6b444922a7cc 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -21,10 +21,7 @@ pub(super) fn check<'tcx>( cx, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, e.span, - &format!( - "transmute from `{}` to `{}` which could be expressed as a pointer cast instead", - from_ty, to_ty - ), + &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { let sugg = arg.as_ty(&to_ty.to_string()).to_string(); diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index d8e349af7af8..19ce5ae72c24 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -1,8 +1,6 @@ use clippy_utils::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_path_diagnostic_item; -use if_chain::if_chain; -use rustc_ast::LitKind; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -19,37 +17,28 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); - if_chain! { - if let ExprKind::Path(ref _qpath) = arg.kind; - if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg); - if x == 0; - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); - return true; - } + if let ExprKind::Path(ref _qpath) = arg.kind && + let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) && + x == 0 + { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; } // Catching: // `std::mem::transmute(0 as *const i32)` - if_chain! { - if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind; - if let ExprKind::Lit(ref lit) = inner_expr.kind; - if let LitKind::Int(0, _) = lit.node; - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); - return true; - } + if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind && is_integer_literal(inner_expr, 0) { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; } // Catching: // `std::mem::transmute(std::ptr::null::())` - if_chain! { - if let ExprKind::Call(func1, []) = arg.kind; - if is_path_diagnostic_item(cx, func1, sym::ptr_null); - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); - return true; - } + if let ExprKind::Call(func1, []) = arg.kind && + is_path_diagnostic_item(cx, func1, sym::ptr_null) + { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; } // FIXME: diff --git a/clippy_lints/src/transmute/unsound_collection_transmute.rs b/clippy_lints/src/transmute/unsound_collection_transmute.rs index 831b0d450d20..b1445311b711 100644 --- a/clippy_lints/src/transmute/unsound_collection_transmute.rs +++ b/clippy_lints/src/transmute/unsound_collection_transmute.rs @@ -37,10 +37,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, UNSOUND_COLLECTION_TRANSMUTE, e.span, - &format!( - "transmute from `{}` to `{}` with mismatched layout is unsound", - from_ty, to_ty - ), + &format!("transmute from `{from_ty}` to `{to_ty}` with mismatched layout is unsound"), ); true } else { diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index 8122cd716e01..f919bbd5afca 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( cx, USELESS_TRANSMUTE, e.span, - &format!("transmute from a type (`{}`) to itself", from_ty), + &format!("transmute from a type (`{from_ty}`) to itself"), ); true }, diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index fdf847bf4459..b567d92230bb 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,8 +1,11 @@ use rustc_hir::Expr; +use rustc_hir_analysis::check::{ + cast::{self, CastCheckResult}, + FnCtxt, Inherited, +}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; -use rustc_hir_analysis::check::{cast::{self, CastCheckResult}, FnCtxt, Inherited}; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment diff --git a/clippy_lints/src/transmute/wrong_transmute.rs b/clippy_lints/src/transmute/wrong_transmute.rs index 2118f3d69500..d1965565b926 100644 --- a/clippy_lints/src/transmute/wrong_transmute.rs +++ b/clippy_lints/src/transmute/wrong_transmute.rs @@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty cx, WRONG_TRANSMUTE, e.span, - &format!("transmute from a `{}` to a pointer", from_ty), + &format!("transmute from a `{from_ty}` to a pointer"), ); true }, diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 1268c23206a6..9c6629958401 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -49,15 +49,15 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m let inner_snippet = snippet(cx, inner.span, ".."); let suggestion = match &inner.kind { TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => { - format!("&{}({})", ltopt, &inner_snippet) + format!("&{ltopt}({})", &inner_snippet) }, TyKind::Path(qpath) if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) .map_or(false, |bounds| bounds.len() > 1) => { - format!("&{}({})", ltopt, &inner_snippet) + format!("&{ltopt}({})", &inner_snippet) }, - _ => format!("&{}{}", ltopt, &inner_snippet), + _ => format!("&{ltopt}{}", &inner_snippet), }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index ba51404d2148..08020ce66381 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -16,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ _ => "<..>", }; - let box_content = format!("{outer}{generic}", outer = item_type); + let box_content = format!("{item_type}{generic}"); span_lint_and_help( cx, BOX_COLLECTION, diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index aca55817c525..79c31efb9fcd 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -352,8 +352,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { match item.kind { ImplItemKind::Const(ty, _) => { - let is_in_trait_impl = if let Some(hir::Node::Item(item)) = - cx.tcx.hir().find_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()).def_id) + let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx + .tcx + .hir() + .find_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()).def_id) { matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) } else { @@ -535,7 +537,7 @@ impl Types { QPath::LangItem(..) => {}, } }, - TyKind::Rptr(ref lt, ref mut_ty) => { + TyKind::Rptr(lt, ref mut_ty) => { context.is_nested_call = true; if !borrowed_box::check(cx, hir_ty, lt, mut_ty) { self.check_ty(cx, mut_ty.ty, context); diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index 4d72a29e8c74..6b9de64e24c9 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ hir_ty.span, "usage of `Rc` when T is a buffer type", "try", - format!("Rc<{}>", alternate), + format!("Rc<{alternate}>"), Applicability::MachineApplicable, ); } else { @@ -57,7 +57,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ hir_ty.span, "usage of `Arc` when T is a buffer type", "try", - format!("Arc<{}>", alternate), + format!("Arc<{alternate}>"), Applicability::MachineApplicable, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index d81c5c83845d..ecb672005390 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -3,9 +3,9 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use rustc_hir_analysis::hir_ty_to_ty; use super::{utils, REDUNDANT_ALLOCATION}; @@ -27,13 +27,11 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{}<{}>`", outer_sym, generic_snippet), + &format!("usage of `{outer_sym}<{generic_snippet}>`"), |diag| { - diag.span_suggestion(hir_ty.span, "try", format!("{}", generic_snippet), applicability); + diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability); diag.note(&format!( - "`{generic}` is already a pointer, `{outer}<{generic}>` allocates a pointer on the heap", - outer = outer_sym, - generic = generic_snippet + "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap" )); }, ); @@ -72,19 +70,16 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet), + &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { diag.span_suggestion( hir_ty.span, "try", - format!("{}<{}>", outer_sym, generic_snippet), + format!("{outer_sym}<{generic_snippet}>"), applicability, ); diag.note(&format!( - "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation", - outer = outer_sym, - inner = inner_sym, - generic = generic_snippet + "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); }, ); @@ -94,19 +89,13 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, REDUNDANT_ALLOCATION, hir_ty.span, - &format!("usage of `{}<{}<{}>>`", outer_sym, inner_sym, generic_snippet), + &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { diag.note(&format!( - "`{inner}<{generic}>` is already on the heap, `{outer}<{inner}<{generic}>>` makes an extra allocation", - outer = outer_sym, - inner = inner_sym, - generic = generic_snippet + "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); diag.help(&format!( - "consider using just `{outer}<{generic}>` or `{inner}<{generic}>`", - outer = outer_sym, - inner = inner_sym, - generic = generic_snippet + "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`" )); }, ); diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 236f9955722d..6c329d8cdf19 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -4,11 +4,11 @@ use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::TypeVisitable; use rustc_span::symbol::sym; -use rustc_hir_analysis::hir_ty_to_ty; use super::VEC_BOX; diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 3f99bd3f3156..1ab0162a8813 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; -use clippy_utils::{is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; +use clippy_utils::{is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -211,9 +211,12 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt } }); match expr.kind { - ExprKind::MethodCall(path, self_expr, [_], _) => { + ExprKind::MethodCall(path, self_expr, [arg], _) => { let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs(); - if is_type_diagnostic_item(cx, self_type, sym::Vec) && path.ident.name.as_str() == "set_len" { + if is_type_diagnostic_item(cx, self_type, sym::Vec) + && path.ident.name.as_str() == "set_len" + && !is_integer_literal(arg, 0) + { Some((self_expr, expr.span)) } else { None diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index c0a4f3fbacd6..57aff5367dd1 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -157,8 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { span, &format!( "this closure returns \ - the unit type which also implements {}", - trait_name + the unit type which also implements {trait_name}" ), ); }, @@ -169,8 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { span, &format!( "this closure returns \ - the unit type which also implements {}", - trait_name + the unit type which also implements {trait_name}" ), Some(last_semi), "probably caused by this trailing semicolon", diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index a6f777abc6e9..f6d3fb00f4ee 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -74,7 +74,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp cx, UNIT_ARG, expr.span, - &format!("passing {}unit value{} to a function", singular, plural), + &format!("passing {singular}unit value{plural} to a function"), |db| { let mut or = ""; args_to_recover @@ -129,7 +129,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( - &format!("use {}unit literal{} instead", singular, plural), + &format!("use {singular}unit literal{plural} instead"), args_to_recover .iter() .map(|arg| (arg.span, "()".to_string())) @@ -143,8 +143,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp db.span_suggestion( expr.span, &format!( - "{}move the expression{} in front of the call and replace {} with the unit literal `()`", - or, empty_or_s, it_or_them + "{or}move the expression{empty_or_s} in front of the call and replace {it_or_them} with the unit literal `()`" ), sugg, applicability, diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index 1dd8895ebd07..226495dcbda3 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, UNIT_CMP, macro_call.span, - &format!("`{}` of unit values detected. This will always {}", macro_name, result), + &format!("`{macro_name}` of unit values detected. This will always {result}"), ); } return; @@ -40,9 +40,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { UNIT_CMP, expr.span, &format!( - "{}-comparison of unit values detected. This will always be {}", - op.as_str(), - result + "{}-comparison of unit values detected. This will always be {result}", + op.as_str() ), ); } diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 8a4f4c0ad971..016aacbf9da3 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -3,7 +3,7 @@ use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { ); } else { if_chain! { - if match_def_path(cx, fun_def_id, &paths::FROM_FROM); + if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(fun_def_id); if let [.., last_arg] = args; if let ExprKind::Lit(spanned) = &last_arg.kind; if let LitKind::Str(symbol, _) = spanned.node; diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs index 839a4bdab09d..bc0dd263d88a 100644 --- a/clippy_lints/src/unnecessary_self_imports.rs +++ b/clippy_lints/src/unnecessary_self_imports.rs @@ -57,7 +57,7 @@ impl EarlyLintPass for UnnecessarySelfImports { format!( "{}{};", last_segment.ident, - if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {}", alias) } else { String::new() }, + if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {alias}") } else { String::new() }, ), Applicability::MaybeIncorrect, ); diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 2c40827db0e7..7211e6864f3a 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::{contains_return, is_lang_ctor, return_ty, visitors::find_all_ret_expressions}; +use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty, visitors::find_all_ret_expressions}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -120,9 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if !ret_expr.span.from_expansion(); // Check if a function call. if let ExprKind::Call(func, [arg]) = ret_expr.kind; - // Check if OPTION_SOME or RESULT_OK, depending on return type. - if let ExprKind::Path(qpath) = &func.kind; - if is_lang_ctor(cx, qpath, lang_item); + if is_res_lang_ctor(cx, path_res(cx, func), lang_item); // Make sure the function argument does not contain a return expression. if !contains_return(arg); then { @@ -153,11 +151,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ) } else { ( - format!( - "this function's return value is unnecessarily wrapped by `{}`", - return_type_label - ), - format!("remove `{}` from the return type...", return_type_label), + format!("this function's return value is unnecessarily wrapped by `{return_type_label}`"), + format!("remove `{return_type_label}` from the return type..."), inner_type.to_string(), "...and then change returning expressions", ) diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 64f7a055cd9b..32cd46812014 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -65,10 +65,7 @@ fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, cx, UNSAFE_REMOVED_FROM_NAME, span, - &format!( - "removed `unsafe` from the name of `{}` in use as `{}`", - old_str, new_str - ), + &format!("removed `unsafe` from the name of `{old_str}` in use as `{new_str}`"), ); } } diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index b38d71784fcf..8bcdff66331d 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::{is_try, match_trait_method, paths}; +use clippy_utils::{is_trait_method, is_try, match_trait_method, paths}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -116,13 +117,13 @@ fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Exp match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT) } else { - match_trait_method(cx, call, &paths::IO_READ) + is_trait_method(cx, call, sym::IoRead) }; let write_trait = if is_await { match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT) || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) } else { - match_trait_method(cx, call, &paths::IO_WRITE) + is_trait_method(cx, call, sym::IoWrite) }; match (read_trait, write_trait, symbol, is_await) { diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index b8a5d4ea8c9f..3164937293b6 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -58,8 +58,8 @@ impl EarlyLintPass for UnusedRounding { cx, UNUSED_ROUNDING, expr.span, - &format!("used the `{}` method with a whole number float", method_name), - &format!("remove the `{}` method call", method_name), + &format!("used the `{method_name}` method with a whole number float"), + &format!("remove the `{method_name}` method call"), float, Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 3ef265580797..ea878043c04e 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -257,9 +257,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { expr.hir_id, expr.span, &format!( - "called `{}` on `{}` after checking its variant with `{}`", + "called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`", method_name.ident.name, - unwrappable_variable_name, unwrappable.check_name.ident.as_str(), ), |diag| { @@ -268,9 +267,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), "try", format!( - "if let {} = {}", - suggested_pattern, - unwrappable_variable_name, + "if let {suggested_pattern} = {unwrappable_variable_name}", ), // We don't track how the unwrapped value is used inside the // block or suggest deleting the unwrap, so we can't offer a diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index baa53ba664f6..a69719b127b2 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{method_chain_args, return_ty}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Expr, ImplItemKind}; +use rustc_hir::ImplItemKind; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -73,51 +73,37 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { } } -struct FindExpectUnwrap<'a, 'tcx> { - lcx: &'a LateContext<'tcx>, - typeck_results: &'tcx ty::TypeckResults<'tcx>, - result: Vec, -} - -impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - // check for `expect` - if let Some(arglists) = method_chain_args(expr, &["expect"]) { - let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) - { - self.result.push(expr.span); +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { + if let ImplItemKind::Fn(_, body_id) = impl_item.kind { + let body = cx.tcx.hir().body(body_id); + let typeck = cx.tcx.typeck(impl_item.def_id.def_id); + let mut result = Vec::new(); + let _: Option = for_each_expr(body.value, |e| { + // check for `expect` + if let Some(arglists) = method_chain_args(e, &["expect"]) { + let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); + if is_type_diagnostic_item(cx, receiver_ty, sym::Option) + || is_type_diagnostic_item(cx, receiver_ty, sym::Result) + { + result.push(e.span); + } } - } - // check for `unwrap` - if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { - let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option) - || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result) - { - self.result.push(expr.span); + // check for `unwrap` + if let Some(arglists) = method_chain_args(e, &["unwrap"]) { + let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); + if is_type_diagnostic_item(cx, receiver_ty, sym::Option) + || is_type_diagnostic_item(cx, receiver_ty, sym::Result) + { + result.push(e.span); + } } - } - // and check sub-expressions - intravisit::walk_expr(self, expr); - } -} - -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { - if let ImplItemKind::Fn(_, body_id) = impl_item.kind { - let body = cx.tcx.hir().body(body_id); - let mut fpu = FindExpectUnwrap { - lcx: cx, - typeck_results: cx.tcx.typeck(impl_item.def_id.def_id), - result: Vec::new(), - }; - fpu.visit_expr(body.value); + ControlFlow::Continue(()) + }); // if we've found one, lint - if !fpu.result.is_empty() { + if !result.is_empty() { span_lint_and_then( cx, UNWRAP_IN_RESULT, @@ -125,7 +111,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc "used unwrap or expect in a function that returns result or option", move |diag| { diag.help("unwrap and expect should not be used in a function that returns result or option"); - diag.span_note(fpu.result, "potential non-recoverable error(s)"); + diag.span_note(result, "potential non-recoverable error(s)"); }, ); } diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 2c71f35d490c..654ea306793b 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -93,7 +93,7 @@ fn check_ident(cx: &LateContext<'_>, ident: &Ident, be_aggressive: bool) { cx, UPPER_CASE_ACRONYMS, span, - &format!("name `{}` contains a capitalized acronym", ident), + &format!("name `{ident}` contains a capitalized acronym"), "consider making the acronym lowercase, except the initial letter", corrected, Applicability::MaybeIncorrect, @@ -114,6 +114,7 @@ impl LateLintPass<'_> for UpperCaseAcronyms { check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive); }, ItemKind::Enum(ref enumdef, _) => { + check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive); // check enum variants separately because again we only want to lint on private enums and // the fn check_variant does not know about the vis of the enum of its variants enumdef diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 2c4f5075e980..65f1b5462081 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -12,11 +12,11 @@ use rustc_hir::{ Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, TyKind, }; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index f1b6463ad0f7..a82643a59f97 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -118,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), None, "consider removing `.try_into()`", ); @@ -146,7 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), None, &hint, ); @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } if_chain! { - if match_def_path(cx, def_id, &paths::FROM_FROM); + if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(def_id); if same_type_and_consts(a, b); then { @@ -165,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - &format!("useless conversion to the same type: `{}`", b), + &format!("useless conversion to the same type: `{b}`"), &sugg_msg, sugg.to_string(), Applicability::MachineApplicable, // snippet diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 1df3135c962d..e069de8cb5c7 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -739,7 +739,7 @@ fn path_to_string(path: &QPath<'_>) -> String { *s += ", "; write!(s, "{:?}", segment.ident.as_str()).unwrap(); }, - other => write!(s, "/* unimplemented: {:?}*/", other).unwrap(), + other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), }, QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"), } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 2be3fa99c811..668123e4d6e3 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -39,28 +39,28 @@ pub struct Rename { pub rename: String, } -/// A single disallowed method, used by the `DISALLOWED_METHODS` lint. #[derive(Clone, Debug, Deserialize)] #[serde(untagged)] -pub enum DisallowedMethod { +pub enum DisallowedPath { Simple(String), WithReason { path: String, reason: Option }, } -impl DisallowedMethod { +impl DisallowedPath { pub fn path(&self) -> &str { let (Self::Simple(path) | Self::WithReason { path, .. }) = self; path } -} -/// A single disallowed type, used by the `DISALLOWED_TYPES` lint. -#[derive(Clone, Debug, Deserialize)] -#[serde(untagged)] -pub enum DisallowedType { - Simple(String), - WithReason { path: String, reason: Option }, + pub fn reason(&self) -> Option<&str> { + match self { + Self::WithReason { + reason: Some(reason), .. + } => Some(reason), + _ => None, + } + } } /// Conf with parse errors @@ -213,7 +213,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), @@ -315,14 +315,18 @@ define_Conf! { /// /// Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports: bool = false), + /// Lint: DISALLOWED_MACROS. + /// + /// The list of disallowed macros, written as fully qualified paths. + (disallowed_macros: Vec = Vec::new()), /// Lint: DISALLOWED_METHODS. /// /// The list of disallowed methods, written as fully qualified paths. - (disallowed_methods: Vec = Vec::new()), + (disallowed_methods: Vec = Vec::new()), /// Lint: DISALLOWED_TYPES. /// /// The list of disallowed types, written as fully qualified paths. - (disallowed_types: Vec = Vec::new()), + (disallowed_types: Vec = Vec::new()), /// Lint: UNREADABLE_LITERAL. /// /// Should the fraction of a decimal be linted to include separators. @@ -362,7 +366,7 @@ define_Conf! { /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), /// Lint: AWAIT_HOLDING_INVALID_TYPE - (await_holding_invalid_types: Vec = Vec::new()), + (await_holding_invalid_types: Vec = Vec::new()), /// Lint: LARGE_INCLUDE_FILE. /// /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes @@ -482,16 +486,13 @@ pub fn format_error(error: Box) -> String { let field = fields.get(index).copied().unwrap_or_default(); write!( msg, - "{:separator_width$}{:field_width$}", - " ", - field, - separator_width = SEPARATOR_WIDTH, - field_width = column_width + "{:SEPARATOR_WIDTH$}{field:column_width$}", + " " ) .unwrap(); } } - write!(msg, "\n{}", suffix).unwrap(); + write!(msg, "\n{suffix}").unwrap(); msg } else { s diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 78c036186f50..85bcbc7ad236 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -2,11 +2,11 @@ use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::match_type; use clippy_utils::{ - def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, - method_calls, paths, peel_blocks_with_stmt, SpanlessEq, + def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_any_def_paths, + match_def_path, method_calls, paths, peel_blocks_with_stmt, peel_hir_expr_refs, SpanlessEq, }; use if_chain::if_chain; use rustc_ast as ast; @@ -15,26 +15,29 @@ use rustc_ast::visit::FnKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::Visitor; use rustc_hir::{ - BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, + BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, TyKind, UnOp, }; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; -use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy}; +use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; +use rustc_middle::ty::{ + self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty, +}; use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{sym, BytePos, Span}; -use rustc_hir_analysis::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; +use std::str; #[cfg(feature = "internal")] pub mod metadata_collector; @@ -226,11 +229,11 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for calls to `utils::match_type()` on a type diagnostic item - /// and suggests to use `utils::is_type_diagnostic_item()` instead. + /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. /// /// ### Why is this bad? - /// `utils::is_type_diagnostic_item()` does not require hardcoded paths. + /// The path for an item is subject to change and is less efficient to look up than a + /// diagnostic item or a `LangItem`. /// /// ### Example /// ```rust,ignore @@ -241,9 +244,9 @@ declare_clippy_lint! { /// ```rust,ignore /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) /// ``` - pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + pub UNNECESSARY_DEF_PATH, internal, - "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" + "using a def path when a diagnostic item or a `LangItem` is available" } declare_clippy_lint! { @@ -530,14 +533,14 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, LINT_WITHOUT_LINT_PASS, lint_span, - &format!("the lint `{}` is not added to any `LintPass`", lint_name), + &format!("the lint `{lint_name}` is not added to any `LintPass`"), ); } } } } -fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool { +fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Rptr( _, MutTy { @@ -666,7 +669,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { path.ident.span, "usage of a compiler lint function", None, - &format!("please use the Clippy variant of this function: `{}`", sugg), + &format!("please use the Clippy variant of this function: `{sugg}`"), ); } } @@ -854,13 +857,8 @@ fn suggest_help( "this call is collapsible", "collapse into", format!( - "span_lint_and_help({}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - &option_span, - help + "span_lint_and_help({}, {}, {}, {}, {}, {help})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, ), Applicability::MachineApplicable, ); @@ -886,107 +884,238 @@ fn suggest_note( "this call is collapsible", "collapse into", format!( - "span_lint_and_note({}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - note_span, - note + "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, ), Applicability::MachineApplicable, ); } -declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]); +declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); -impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { +#[allow(clippy::too_many_lines)] +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) { + enum Item { + LangItem(Symbol), + DiagnosticItem(Symbol), + } + static PATHS: &[&[&str]] = &[ + &["clippy_utils", "match_def_path"], + &["clippy_utils", "match_trait_method"], + &["clippy_utils", "ty", "match_type"], + &["clippy_utils", "is_expr_path_def_path"], + ]; + + if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { return; } if_chain! { - // Check if this is a call to utils::match_type() - if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; - if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]); + if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind; + if let ExprKind::Path(path) = &func.kind; + if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if let Some(which_path) = match_any_def_paths(cx, id, PATHS); + let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, ty_path); - let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect(); - if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id(); - // Check if the matched type is a diagnostic item - if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did); + if let Some(segments) = path_to_matched_type(cx, item_arg); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id(); then { - // TODO: check paths constants from external crates. - let cx_snippet = snippet(cx, context.span, "_"); - let ty_snippet = snippet(cx, ty.span, "_"); + // def_path_res will match field names before anything else, but for this we want to match + // inherent functions first. + let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field { + let method_name = *segments.last().unwrap(); + cx.tcx.def_key(def_id).parent + .and_then(|parent_idx| + cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter() + .find_map(|impl_id| cx.tcx.associated_items(*impl_id) + .find_by_name_and_kind( + cx.tcx, + Ident::from_str(method_name), + AssocKind::Fn, + *impl_id, + ) + ) + ) + .map_or(def_id, |item| item.def_id) + } else { + def_id + }; - span_lint_and_sugg( + // Check if the target item is a diagnostic item or LangItem. + let (msg, item) = if let Some(item_name) + = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) + { + ( + "use of a def path to a diagnostic item", + Item::DiagnosticItem(*item_name), + ) + } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { + let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); + let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name; + ( + "use of a def path to a `LangItem`", + Item::LangItem(item_name), + ) + } else { + return; + }; + + let has_ctor = match cx.tcx.def_kind(def_id) { + DefKind::Struct => { + let variant = cx.tcx.adt_def(def_id).non_enum_variant(); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + } + DefKind::Variant => { + let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + } + _ => false, + }; + + let mut app = Applicability::MachineApplicable; + let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); + let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); + let (sugg, with_note) = match (which_path, item) { + // match_def_path + (0, Item::DiagnosticItem(item)) => + (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor), + (0, Item::LangItem(item)) => ( + format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), + has_ctor + ), + // match_trait_method + (1, Item::DiagnosticItem(item)) => + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false), + // match_type + (2, Item::DiagnosticItem(item)) => + (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), + (2, Item::LangItem(item)) => + (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false), + // is_expr_path_def_path + (3, Item::DiagnosticItem(item)) if has_ctor => ( + format!( + "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})", + ), + false, + ), + (3, Item::LangItem(item)) if has_ctor => ( + format!( + "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})", + ), + false, + ), + (3, Item::DiagnosticItem(item)) => + (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), + (3, Item::LangItem(item)) => ( + format!( + "path_res({cx_snip}, {def_snip}).opt_def_id()\ + .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", + ), + false, + ), + _ => return, + }; + + span_lint_and_then( cx, - MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + UNNECESSARY_DEF_PATH, expr.span, - "usage of `clippy_utils::ty::match_type()` on a type diagnostic item", - "try", - format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), - Applicability::MaybeIncorrect, + msg, + |diag| { + diag.span_suggestion(expr.span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead" + ); + } + }, ); } } } } -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { - use rustc_hir::ItemKind; - - match &expr.kind { - ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr), - ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) { +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + match peel_hir_expr_refs(expr).0.kind { + ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { let parent_id = cx.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) { - if let Some(init) = local.init { - return path_to_matched_type(cx, init); - } + if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { + path_to_matched_type(cx, init) + } else { + None } }, - Res::Def(DefKind::Const | DefKind::Static(..), def_id) => { - if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) { - if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind { - let body = cx.tcx.hir().body(body_id); - return path_to_matched_type(cx, body.value); - } - } + Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( + cx, + cx.tcx.eval_static_initializer(def_id).ok()?.inner(), + cx.tcx.type_of(def_id), + ), + Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { + ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { + read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) + }, + _ => None, }, - _ => {}, + _ => None, }, - ExprKind::Array(exprs) => { - let segments: Vec = exprs - .iter() - .filter_map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some(sym); - } + ExprKind::Array(exprs) => exprs + .iter() + .map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some((*sym.as_str()).to_owned()); } + } - None - }) - .collect(); - - if segments.len() == exprs.len() { - return Some(segments); - } - }, - _ => {}, + None + }) + .collect(), + _ => None, } +} + +fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { + let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { + let &alloc = alloc.provenance().values().next()?; + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + (alloc.inner(), ty) + } else { + return None; + } + } else { + (alloc, ty) + }; - None + if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() + && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() + && ty.is_str() + { + alloc + .provenance() + .values() + .map(|&alloc| { + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + let alloc = alloc.inner(); + str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) + .ok().map(ToOwned::to_owned) + } else { + None + } + }) + .collect() + } else { + None + } } // This is not a complete resolver for paths. It works on all the paths currently used in the paths // module. That's all it does and all it needs to do. pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if def_path_res(cx, path) != Res::Err { + if def_path_res(cx, path, None) != Res::Err { return true; } @@ -1077,7 +1206,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module).opt_def_id() { + if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { for item in cx.tcx.module_children(def_id).iter() { if_chain! { if let Res::Def(DefKind::Const, item_def_id) = item.res; diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 342f627e3827..c84191bb0103 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -64,46 +64,6 @@ const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[ /// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed /// to only keep the actual lint group in the output. const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::"; - -/// This template will be used to format the configuration section in the lint documentation. -/// The `configurations` parameter will be replaced with one or multiple formatted -/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations -macro_rules! CONFIGURATION_SECTION_TEMPLATE { - () => { - r#" -### Configuration -This lint has the following configuration variables: - -{configurations} -"# - }; -} -/// This template will be used to format an individual `ClippyConfiguration` instance in the -/// lint documentation. -/// -/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and -/// `default` -macro_rules! CONFIGURATION_VALUE_TEMPLATE { - () => { - "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n" - }; -} - -macro_rules! RENAMES_SECTION_TEMPLATE { - () => { - r#" -### Past names - -{names} -"# - }; -} -macro_rules! RENAME_VALUE_TEMPLATE { - () => { - "* `{name}`\n" - }; -} - const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], @@ -205,7 +165,16 @@ impl MetadataCollector { .filter(|config| config.lints.iter().any(|lint| lint == lint_name)) .map(ToString::to_string) .reduce(|acc, x| acc + &x) - .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations)) + .map(|configurations| { + format!( + r#" +### Configuration +This lint has the following configuration variables: + +{configurations} +"# + ) + }) } } @@ -291,16 +260,13 @@ fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Pa continue; } - panic!("lint `{}` has an unterminated code block", lint_name) + panic!("lint `{lint_name}` has an unterminated code block") } break; }, Some(line) if line.trim_start() == "{{produces}}" => { - panic!( - "lint `{}` has marker {{{{produces}}}} with an ignored or missing code block", - lint_name - ) + panic!("lint `{lint_name}` has marker {{{{produces}}}} with an ignored or missing code block") }, Some(line) => { let line = line.trim(); @@ -319,7 +285,7 @@ fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Pa match lines.next() { Some(line) if line.trim_start() == "```" => break, Some(line) => example.push(line), - None => panic!("lint `{}` has an unterminated code block", lint_name), + None => panic!("lint `{lint_name}` has an unterminated code block"), } } @@ -336,10 +302,9 @@ fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Pa Produces\n\ \n\ ```text\n\ - {}\n\ + {output}\n\ ```\n\ - ", - output + " ), ); @@ -394,7 +359,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy()); } - let prefixed_name = format!("{}{lint_name}", CLIPPY_LINT_GROUP_PREFIX); + let prefixed_name = format!("{CLIPPY_LINT_GROUP_PREFIX}{lint_name}"); let mut cmd = Command::new("cargo"); @@ -417,7 +382,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root let output = cmd .arg(file.as_path()) .output() - .unwrap_or_else(|e| panic!("failed to run `{:?}`: {e}", cmd)); + .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}")); let tmp_file_path = file.to_string_lossy(); let stderr = std::str::from_utf8(&output.stderr).unwrap(); @@ -441,8 +406,7 @@ fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect(); panic!( - "did not find lint `{}` in output of example, got:\n{}\n{}", - lint_name, + "did not find lint `{lint_name}` in output of example, got:\n{}\n{}", non_json.join("\n"), rendered.join("\n") ); @@ -588,13 +552,10 @@ fn to_kebab(config_name: &str) -> String { impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { - write!( + writeln!( f, - CONFIGURATION_VALUE_TEMPLATE!(), - name = self.name, - ty = self.config_type, - doc = self.doc, - default = self.default + "* `{}`: `{}`: {} (defaults to `{}`)", + self.name, self.config_type, self.doc, self.default ) } } @@ -811,7 +772,7 @@ fn get_lint_group_and_level_or_lint( lint_collection_error_item( cx, item, - &format!("Unable to determine lint level for found group `{}`", group), + &format!("Unable to determine lint level for found group `{group}`"), ); None } @@ -869,7 +830,7 @@ fn collect_renames(lints: &mut Vec) { if name == lint_name; if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); then { - write!(collected, RENAME_VALUE_TEMPLATE!(), name = past_name).unwrap(); + writeln!(collected, "* `{past_name}`").unwrap(); names.push(past_name.to_string()); } } @@ -882,7 +843,15 @@ fn collect_renames(lints: &mut Vec) { } if !collected.is_empty() { - write!(&mut lint.docs, RENAMES_SECTION_TEMPLATE!(), names = collected).unwrap(); + write!( + &mut lint.docs, + r#" +### Past names + +{collected} +"# + ) + .unwrap(); } } } @@ -895,7 +864,7 @@ fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &s cx, INTERNAL_METADATA_COLLECTOR, item.ident.span, - &format!("metadata collection error for `{}`: {}", item.ident.name, message), + &format!("metadata collection error for `{}`: {message}", item.ident.name), ); } diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 2604b1ee7c56..301eed9a1fbf 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -173,7 +173,7 @@ impl LateLintPass<'_> for WildcardImports { let sugg = if braced_glob { imports_string } else { - format!("{}::{}", import_source_snippet, imports_string) + format!("{import_source_snippet}::{imports_string}") }; let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res { diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 06e7d7017017..36574198f917 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{sym, BytePos}; declare_clippy_lint! { /// ### What it does @@ -475,11 +475,11 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: & value.span, "literal with an empty format string", |diag| { - if let Some(replacement) = replacement { + if let Some(replacement) = replacement // `format!("{}", "a")`, `format!("{named}", named = "b") // ~~~~~ ~~~~~~~~~~~~~ - let value_span = expand_past_previous_comma(cx, value.span); - + && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id) + { let replacement = replacement.replace('{', "{{").replace('}', "}}"); diag.multipart_suggestion( "try this", @@ -542,10 +542,3 @@ fn conservative_unescape(literal: &str) -> Result { if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) } } - -// Expand from `writeln!(o, "")` to `writeln!(o, "")` -// ^^ ^^^^ -fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { - let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); - extended.with_lo(extended.lo() - BytePos(1)) -} diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 50d3c079fe67..9b3de35dbd3c 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -57,8 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { "constant division of `0.0` with `0.0` will always result in NaN", None, &format!( - "consider using `{}::NAN` if you would like a constant representing NaN", - float_type, + "consider using `{float_type}::NAN` if you would like a constant representing NaN", ), ); } diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 703ba2ef4b05..6cf2a955fd5c 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item}; use if_chain::if_chain; use rustc_hir::{self as hir, HirId, ItemKind, Node}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::{Adt, Ty, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; -use rustc_hir_analysis::hir_ty_to_ty; declare_clippy_lint! { /// ### What it does @@ -69,10 +69,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { let parent_id = cx.tcx.hir().get_parent_item(hir_id); - let second_parent_id = cx - .tcx - .hir() - .get_parent_item(parent_id.into()).def_id; + let second_parent_id = cx.tcx.hir().get_parent_item(parent_id.into()).def_id; if let Some(Node::Item(item)) = cx.tcx.hir().find_by_def_id(second_parent_id) { if let ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { return true; diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index c36bca06507d..83fee7bb39c2 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.65" +version = "0.1.66" edition = "2021" publish = false diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 8ab77c881663..d9b22664fd25 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -131,12 +131,12 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s match attr.style { ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), ast::AttrStyle::Inner => { - sess.struct_span_err(attr.span, &format!("`{}` is defined multiple times", name)) + sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times")) .span_note(unique_attr.as_ref().unwrap().span, "first definition found here") .emit(); }, ast::AttrStyle::Outer => { - sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name)); + sess.span_err(attr.span, &format!("`{name}` cannot be an outer attribute")); }, } } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 78960d1ab1da..78f93755b72d 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -18,12 +18,11 @@ fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { diag.help(&format!( - "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}", + "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { // extract just major + minor version and ignore patch versions format!("rust-{}", n.rsplit_once('.').unwrap().1) - }), - lint + }) )); } } diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 91c9c382c236..8724a4cd651d 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -113,7 +113,17 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, args, ) => match self.cx.qpath_res(path, hir_id) { - Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => (), + Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => { + if self + .cx + .typeck_results() + .expr_ty(e) + .has_significant_drop(self.cx.tcx, self.cx.param_env) + { + self.eagerness = Lazy; + return; + } + }, Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (), // No need to walk the arguments here, `is_const_evaluatable` already did Res::Def(..) if is_const_evaluatable(self.cx, e) => { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 7212d9cd7445..cf24ec8b67b9 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -962,7 +962,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { mut_ty.mutbl.hash(&mut self.s); }, TyKind::Rptr(lifetime, ref mut_ty) => { - self.hash_lifetime(*lifetime); + self.hash_lifetime(lifetime); self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, @@ -992,7 +992,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { in_trait.hash(&mut self.s); }, TyKind::TraitObject(_, lifetime, _) => { - self.hash_lifetime(*lifetime); + self.hash_lifetime(lifetime); }, TyKind::Typeof(anon_const) => { self.hash_body(anon_const.body); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8f79c07c9772..42374fdd7baf 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -3,6 +3,7 @@ #![feature(control_flow_enum)] #![feature(let_chains)] #![feature(lint_reasons)] +#![feature(never_type)] #![feature(once_cell)] #![feature(rustc_private)] #![recursion_limit = "512"] @@ -23,6 +24,7 @@ extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; +extern crate rustc_hir_analysis; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; @@ -32,7 +34,6 @@ extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; extern crate rustc_trait_selection; -extern crate rustc_hir_analysis; #[macro_use] pub mod sym_helper; @@ -65,6 +66,7 @@ pub use self::hir_utils::{ both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash, }; +use core::ops::ControlFlow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use std::sync::OnceLock; @@ -76,7 +78,7 @@ use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; @@ -113,14 +115,14 @@ use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param}; -use crate::visitors::expr_visitor_no_bodies; +use crate::visitors::for_each_expr; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { return Some(version); } else if let Some(sess) = sess { if let Some(span) = span { - sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv)); + sess.span_err(span, &format!("`{msrv}` is not a valid Rust version")); } } None @@ -238,19 +240,69 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { } } -/// Checks if a `QPath` resolves to a constructor of a `LangItem`. +/// Checks if a `Res` refers to a constructor of a `LangItem` /// For example, use this to check whether a function call or a pattern is `Some(..)`. -pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool { +pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool { + if let Res::Def(DefKind::Ctor(..), id) = res + && let Ok(lang_id) = cx.tcx.lang_items().require(lang_item) + && let Some(id) = cx.tcx.opt_parent(id) + { + id == lang_id + } else { + false + } +} + +pub fn is_res_diagnostic_ctor(cx: &LateContext<'_>, res: Res, diag_item: Symbol) -> bool { + if let Res::Def(DefKind::Ctor(..), id) = res + && let Some(id) = cx.tcx.opt_parent(id) + { + cx.tcx.is_diagnostic_item(diag_item, id) + } else { + false + } +} + +/// Checks if a `QPath` resolves to a constructor of a diagnostic item. +pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool { if let QPath::Resolved(_, path) = qpath { if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res { - if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) { - return cx.tcx.parent(ctor_id) == item_id; - } + return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id)); } } false } +/// Checks if the `DefId` matches the given diagnostic item or it's constructor. +pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool { + let did = match cx.tcx.def_kind(did) { + DefKind::Ctor(..) => cx.tcx.parent(did), + // Constructors for types in external crates seem to have `DefKind::Variant` + DefKind::Variant => match cx.tcx.opt_parent(did) { + Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did, + _ => did, + }, + _ => did, + }; + + cx.tcx.is_diagnostic_item(item, did) +} + +/// Checks if the `DefId` matches the given `LangItem` or it's constructor. +pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool { + let did = match cx.tcx.def_kind(did) { + DefKind::Ctor(..) => cx.tcx.parent(did), + // Constructors for types in external crates seem to have `DefKind::Variant` + DefKind::Variant => match cx.tcx.opt_parent(did) { + Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did, + _ => did, + }, + _ => did, + }; + + cx.tcx.lang_items().require(item).map_or(false, |id| id == did) +} + pub fn is_unit_expr(expr: &Expr<'_>) -> bool { matches!( expr.kind, @@ -470,15 +522,49 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -/// Resolves a def path like `std::vec::Vec`. +fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { + let single = |ty| tcx.incoherent_impls(ty).iter().copied(); + let empty = || [].iter().copied(); + match name { + "bool" => single(BoolSimplifiedType), + "char" => single(CharSimplifiedType), + "str" => single(StrSimplifiedType), + "array" => single(ArraySimplifiedType), + "slice" => single(SliceSimplifiedType), + // FIXME: rustdoc documents these two using just `pointer`. + // + // Maybe this is something we should do here too. + "const_ptr" => single(PtrSimplifiedType(Mutability::Not)), + "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)), + "isize" => single(IntSimplifiedType(IntTy::Isize)), + "i8" => single(IntSimplifiedType(IntTy::I8)), + "i16" => single(IntSimplifiedType(IntTy::I16)), + "i32" => single(IntSimplifiedType(IntTy::I32)), + "i64" => single(IntSimplifiedType(IntTy::I64)), + "i128" => single(IntSimplifiedType(IntTy::I128)), + "usize" => single(UintSimplifiedType(UintTy::Usize)), + "u8" => single(UintSimplifiedType(UintTy::U8)), + "u16" => single(UintSimplifiedType(UintTy::U16)), + "u32" => single(UintSimplifiedType(UintTy::U32)), + "u64" => single(UintSimplifiedType(UintTy::U64)), + "u128" => single(UintSimplifiedType(UintTy::U128)), + "f32" => single(FloatSimplifiedType(FloatTy::F32)), + "f64" => single(FloatSimplifiedType(FloatTy::F64)), + _ => empty(), + } +} + +/// Resolves a def path like `std::vec::Vec`. `namespace_hint` can be supplied to disambiguate +/// between `std::vec` the module and `std::vec` the macro +/// /// This function is expensive and should be used sparingly. -pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { - fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option { +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option) -> Res { + fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str, matches_ns: impl Fn(Res) -> bool) -> Option { match tcx.def_kind(def_id) { DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx .module_children(def_id) .iter() - .find(|item| item.ident.name.as_str() == name) + .find(|item| item.ident.name.as_str() == name && matches_ns(item.res.expect_non_local())) .map(|child| child.res.expect_non_local()), DefKind::Impl => tcx .associated_item_def_ids(def_id) @@ -486,40 +572,17 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { .copied() .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name) .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)), + DefKind::Struct | DefKind::Union => tcx + .adt_def(def_id) + .non_enum_variant() + .fields + .iter() + .find(|f| f.name.as_str() == name) + .map(|f| Res::Def(DefKind::Field, f.did)), _ => None, } } - fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { - let single = |ty| tcx.incoherent_impls(ty).iter().copied(); - let empty = || [].iter().copied(); - match name { - "bool" => single(BoolSimplifiedType), - "char" => single(CharSimplifiedType), - "str" => single(StrSimplifiedType), - "array" => single(ArraySimplifiedType), - "slice" => single(SliceSimplifiedType), - // FIXME: rustdoc documents these two using just `pointer`. - // - // Maybe this is something we should do here too. - "const_ptr" => single(PtrSimplifiedType(Mutability::Not)), - "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)), - "isize" => single(IntSimplifiedType(IntTy::Isize)), - "i8" => single(IntSimplifiedType(IntTy::I8)), - "i16" => single(IntSimplifiedType(IntTy::I16)), - "i32" => single(IntSimplifiedType(IntTy::I32)), - "i64" => single(IntSimplifiedType(IntTy::I64)), - "i128" => single(IntSimplifiedType(IntTy::I128)), - "usize" => single(UintSimplifiedType(UintTy::Usize)), - "u8" => single(UintSimplifiedType(UintTy::U8)), - "u16" => single(UintSimplifiedType(UintTy::U16)), - "u32" => single(UintSimplifiedType(UintTy::U32)), - "u64" => single(UintSimplifiedType(UintTy::U64)), - "u128" => single(UintSimplifiedType(UintTy::U128)), - "f32" => single(FloatSimplifiedType(FloatTy::F32)), - "f64" => single(FloatSimplifiedType(FloatTy::F64)), - _ => empty(), - } - } + fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option { tcx.crates(()) .iter() @@ -528,32 +591,45 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { .map(CrateNum::as_def_id) } - let (base, first, path) = match *path { - [base, first, ref path @ ..] => (base, first, path), + let (base, path) = match *path { [primitive] => { return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy); }, + [base, ref path @ ..] => (base, path), _ => return Res::Err, }; let tcx = cx.tcx; let starts = find_primitive(tcx, base) .chain(find_crate(tcx, base)) - .filter_map(|id| item_child_by_name(tcx, id, first)); + .map(|id| Res::Def(tcx.def_kind(id), id)); for first in starts { let last = path .iter() .copied() + .enumerate() // for each segment, find the child item - .try_fold(first, |res, segment| { + .try_fold(first, |res, (idx, segment)| { + let matches_ns = |res: Res| { + // If at the last segment in the path, respect the namespace hint + if idx == path.len() - 1 { + match namespace_hint { + Some(ns) => res.matches_ns(ns), + None => true, + } + } else { + res.matches_ns(Namespace::TypeNS) + } + }; + let def_id = res.def_id(); - if let Some(item) = item_child_by_name(tcx, def_id, segment) { + if let Some(item) = item_child_by_name(tcx, def_id, segment, matches_ns) { Some(item) } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { // it is not a child item so check inherent impl items tcx.inherent_impls(def_id) .iter() - .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment)) + .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment, matches_ns)) } else { None } @@ -569,8 +645,10 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res { /// Convenience function to get the `DefId` of a trait by path. /// It could be a trait or trait alias. +/// +/// This function is expensive and should be used sparingly. pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option { - match def_path_res(cx, path) { + match def_path_res(cx, path, Some(Namespace::TypeNS)) { Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), _ => None, } @@ -738,7 +816,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { } }, ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func), - ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone), + ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, } @@ -1136,17 +1214,14 @@ pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { /// Returns `true` if `expr` contains a return expression pub fn contains_return(expr: &hir::Expr<'_>) -> bool { - let mut found = false; - expr_visitor_no_bodies(|expr| { - if !found { - if let hir::ExprKind::Ret(..) = &expr.kind { - found = true; - } + for_each_expr(expr, |e| { + if matches!(e.kind, hir::ExprKind::Ret(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - !found }) - .visit_expr(expr); - found + .is_some() } /// Extends the span to the beginning of the spans line, incl. whitespaces. @@ -1386,8 +1461,8 @@ pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool { /// Examples of coercions can be found in the Nomicon at /// . /// -/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for more -/// information on adjustments and coercions. +/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for +/// more information on adjustments and coercions. pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { cx.typeck_results().adjustments().get(e.hir_id).is_some() } @@ -1553,7 +1628,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc if_chain! { if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind; if ddpos.as_opt_usize().is_none(); - if is_lang_ctor(cx, path, ResultOk); + if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk); if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind; if path_to_local_id(arm.body, hir_id); then { @@ -1565,7 +1640,7 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind { - is_lang_ctor(cx, path, ResultErr) + is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr) } else { false } @@ -2295,6 +2370,29 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { }); } +/// Return all the comments a given span contains +/// Comments are returned wrapped with their relevant delimiters +pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { + let snippet = sm.span_to_snippet(span).unwrap_or_default(); + let mut comments_buf: Vec = Vec::new(); + let mut index: usize = 0; + + for token in tokenize(&snippet) { + let token_range = index..(index + token.len as usize); + index += token.len as usize; + match token.kind { + TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => { + if let Some(comment) = snippet.get(token_range) { + comments_buf.push(comment.to_string()); + } + }, + _ => (), + } + } + + comments_buf.join("\n") +} + macro_rules! op_utils { ($($name:ident $assign:ident)*) => { /// Binary operation traits like `LangItem::Add` diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index a1808c097200..dd0ce1da6575 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -2,7 +2,7 @@ use crate::is_path_diagnostic_item; use crate::source::snippet_opt; -use crate::visitors::expr_visitor_no_bodies; +use crate::visitors::{for_each_expr, Descend}; use arrayvec::ArrayVec; use itertools::{izip, Either, Itertools}; @@ -16,6 +16,7 @@ use rustc_parse_format::{self as rpf, Alignment}; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol}; +use std::iter::{once, zip}; use std::ops::ControlFlow; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -270,20 +271,19 @@ fn find_assert_args_inner<'a, const N: usize>( }; let mut args = ArrayVec::new(); let mut panic_expn = None; - expr_visitor_no_bodies(|e| { + let _: Option = for_each_expr(expr, |e| { if args.is_full() { if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() { panic_expn = PanicExpn::parse(cx, e); } - panic_expn.is_none() + ControlFlow::Continue(Descend::from(panic_expn.is_none())) } else if is_assert_arg(cx, e, expn) { args.push(e); - false + ControlFlow::Continue(Descend::No) } else { - true + ControlFlow::Continue(Descend::Yes) } - }) - .visit_expr(expr); + }); let args = args.into_inner().ok()?; // if no `panic!(..)` is found, use `PanicExpn::Empty` // to indicate that the default assertion message is used @@ -297,22 +297,19 @@ fn find_assert_within_debug_assert<'a>( expn: ExpnId, assert_name: Symbol, ) -> Option<(&'a Expr<'a>, ExpnId)> { - let mut found = None; - expr_visitor_no_bodies(|e| { - if found.is_some() || !e.span.from_expansion() { - return false; + for_each_expr(expr, |e| { + if !e.span.from_expansion() { + return ControlFlow::Continue(Descend::No); } let e_expn = e.span.ctxt().outer_expn(); if e_expn == expn { - return true; - } - if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) { - found = Some((e, e_expn)); + ControlFlow::Continue(Descend::Yes) + } else if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) { + ControlFlow::Break((e, e_expn)) + } else { + ControlFlow::Continue(Descend::No) } - false }) - .visit_expr(expr); - found } fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool { @@ -392,20 +389,18 @@ impl FormatString { unescape_literal(inner, mode, &mut |_, ch| match ch { Ok(ch) => unescaped.push(ch), Err(e) if !e.is_fatal() => (), - Err(e) => panic!("{:?}", e), + Err(e) => panic!("{e:?}"), }); let mut parts = Vec::new(); - expr_visitor_no_bodies(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(symbol, _) = lit.node { - parts.push(symbol); - } + let _: Option = for_each_expr(pieces, |expr| { + if let ExprKind::Lit(lit) = &expr.kind + && let LitKind::Str(symbol, _) = lit.node + { + parts.push(symbol); } - - true - }) - .visit_expr(pieces); + ControlFlow::Continue(()) + }); Some(Self { span, @@ -418,7 +413,8 @@ impl FormatString { } struct FormatArgsValues<'tcx> { - /// See `FormatArgsExpn::value_args` + /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for + /// `format!("{x} {} {y}", 1, z + 2)`. value_args: Vec<&'tcx Expr<'tcx>>, /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in /// `value_args` @@ -431,7 +427,7 @@ impl<'tcx> FormatArgsValues<'tcx> { fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self { let mut pos_to_value_index = Vec::new(); let mut value_args = Vec::new(); - expr_visitor_no_bodies(|expr| { + let _: Option = for_each_expr(args, |expr| { if expr.span.ctxt() == args.span.ctxt() { // ArgumentV1::new_() // ArgumentV1::from_usize() @@ -453,16 +449,13 @@ impl<'tcx> FormatArgsValues<'tcx> { pos_to_value_index.push(val_idx); } - - true + ControlFlow::Continue(Descend::Yes) } else { // assume that any expr with a differing span is a value value_args.push(expr); - - false + ControlFlow::Continue(Descend::No) } - }) - .visit_expr(args); + }); Self { value_args, @@ -545,19 +538,32 @@ fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span { ) } +/// How a format parameter is used in the format string #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum FormatParamKind { /// An implicit parameter , such as `{}` or `{:?}`. Implicit, - /// A parameter with an explicit number, or an asterisk precision. e.g. `{1}`, `{0:?}`, - /// `{:.0$}` or `{:.*}`. + /// A parameter with an explicit number, e.g. `{1}`, `{0:?}`, or `{:.0$}` Numbered, + /// A parameter with an asterisk precision. e.g. `{:.*}`. + Starred, /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`. Named(Symbol), /// An implicit named parameter, such as the `y` in `format!("{y}")`. NamedInline(Symbol), } +/// Where a format parameter is being used in the format string +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum FormatParamUsage { + /// Appears as an argument, e.g. `format!("{}", foo)` + Argument, + /// Appears as a width, e.g. `format!("{:width$}", foo, width = 1)` + Width, + /// Appears as a precision, e.g. `format!("{:.precision$}", foo, precision = 1)` + Precision, +} + /// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g. /// /// ``` @@ -573,6 +579,8 @@ pub struct FormatParam<'tcx> { pub value: &'tcx Expr<'tcx>, /// How this parameter refers to its `value`. pub kind: FormatParamKind, + /// Where this format param is being used - argument/width/precision + pub usage: FormatParamUsage, /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters. /// /// ```text @@ -585,6 +593,7 @@ pub struct FormatParam<'tcx> { impl<'tcx> FormatParam<'tcx> { fn new( mut kind: FormatParamKind, + usage: FormatParamUsage, position: usize, inner: rpf::InnerSpan, values: &FormatArgsValues<'tcx>, @@ -599,7 +608,12 @@ impl<'tcx> FormatParam<'tcx> { kind = FormatParamKind::NamedInline(name); } - Some(Self { value, kind, span }) + Some(Self { + value, + kind, + usage, + span, + }) } } @@ -618,6 +632,7 @@ pub enum Count<'tcx> { impl<'tcx> Count<'tcx> { fn new( + usage: FormatParamUsage, count: rpf::Count<'_>, position: Option, inner: Option, @@ -625,15 +640,27 @@ impl<'tcx> Count<'tcx> { ) -> Option { Some(match count { rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)), - rpf::Count::CountIsName(name, span) => Self::Param(FormatParam::new( + rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new( FormatParamKind::Named(Symbol::intern(name)), + usage, position?, - span, + inner?, + values, + )?), + rpf::Count::CountIsParam(_) => Self::Param(FormatParam::new( + FormatParamKind::Numbered, + usage, + position?, + inner?, + values, + )?), + rpf::Count::CountIsStar(_) => Self::Param(FormatParam::new( + FormatParamKind::Starred, + usage, + position?, + inner?, values, )?), - rpf::Count::CountIsParam(_) | rpf::Count::CountIsStar(_) => { - Self::Param(FormatParam::new(FormatParamKind::Numbered, position?, inner?, values)?) - }, rpf::Count::CountImplied => Self::Implied, }) } @@ -676,8 +703,20 @@ impl<'tcx> FormatSpec<'tcx> { fill: spec.fill, align: spec.align, flags: spec.flags, - precision: Count::new(spec.precision, positions.precision, spec.precision_span, values)?, - width: Count::new(spec.width, positions.width, spec.width_span, values)?, + precision: Count::new( + FormatParamUsage::Precision, + spec.precision, + positions.precision, + spec.precision_span, + values, + )?, + width: Count::new( + FormatParamUsage::Width, + spec.width, + positions.width, + spec.width_span, + values, + )?, r#trait: match spec.ty { "" => sym::Display, "?" => sym::Debug, @@ -723,17 +762,87 @@ pub struct FormatArg<'tcx> { pub struct FormatArgsExpn<'tcx> { /// The format string literal. pub format_string: FormatString, - // The format arguments, such as `{:?}`. + /// The format arguments, such as `{:?}`. pub args: Vec>, /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will /// include this added newline. pub newline: bool, - /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for + /// Spans of the commas between the format string and explicit values, excluding any trailing + /// comma + /// + /// ```ignore + /// format!("..", 1, 2, 3,) + /// // ^ ^ ^ + /// ``` + comma_spans: Vec, + /// Explicit values passed after the format string, ignoring implicit captures. `[1, z + 2]` for /// `format!("{x} {} {y}", 1, z + 2)`. - value_args: Vec<&'tcx Expr<'tcx>>, + explicit_values: Vec<&'tcx Expr<'tcx>>, } impl<'tcx> FormatArgsExpn<'tcx> { + /// Gets the spans of the commas inbetween the format string and explicit args, not including + /// any trailing comma + /// + /// ```ignore + /// format!("{} {}", a, b) + /// // ^ ^ + /// ``` + /// + /// Ensures that the format string and values aren't coming from a proc macro that sets the + /// output span to that of its input + fn comma_spans(cx: &LateContext<'_>, explicit_values: &[&Expr<'_>], fmt_span: Span) -> Option> { + // `format!("{} {} {c}", "one", "two", c = "three")` + // ^^^^^ ^^^^^ ^^^^^^^ + let value_spans = explicit_values + .iter() + .map(|val| hygiene::walk_chain(val.span, fmt_span.ctxt())); + + // `format!("{} {} {c}", "one", "two", c = "three")` + // ^^ ^^ ^^^^^^ + let between_spans = once(fmt_span) + .chain(value_spans) + .tuple_windows() + .map(|(start, end)| start.between(end)); + + let mut comma_spans = Vec::new(); + for between_span in between_spans { + let mut offset = 0; + let mut seen_comma = false; + + for token in tokenize(&snippet_opt(cx, between_span)?) { + match token.kind { + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {}, + TokenKind::Comma if !seen_comma => { + seen_comma = true; + + let base = between_span.data(); + comma_spans.push(Span::new( + base.lo + BytePos(offset), + base.lo + BytePos(offset + 1), + base.ctxt, + base.parent, + )); + }, + // named arguments, `start_val, name = end_val` + // ^^^^^^^^^ between_span + TokenKind::Ident | TokenKind::Eq if seen_comma => {}, + // An unexpected token usually indicates the format string or a value came from a proc macro output + // that sets the span of its output to an input, e.g. `println!(some_proc_macro!("input"), ..)` that + // emits a string literal with the span set to that of `"input"` + _ => return None, + } + offset += token.len; + } + + if !seen_comma { + return None; + } + } + + Some(comma_spans) + } + pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option { let macro_name = macro_backtrace(expr.span) .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) @@ -797,6 +906,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { // NamedInline is handled by `FormatParam::new()` rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)), }, + FormatParamUsage::Argument, position.value, parsed_arg.position_span, &values, @@ -807,11 +917,22 @@ impl<'tcx> FormatArgsExpn<'tcx> { }) .collect::>>()?; + let mut explicit_values = values.value_args; + // remove values generated for implicitly captured vars + let len = explicit_values + .iter() + .take_while(|val| !format_string.span.contains(val.span)) + .count(); + explicit_values.truncate(len); + + let comma_spans = Self::comma_spans(cx, &explicit_values, format_string.span)?; + Some(Self { format_string, args, - value_args: values.value_args, newline, + comma_spans, + explicit_values, }) } else { None @@ -819,27 +940,25 @@ impl<'tcx> FormatArgsExpn<'tcx> { } pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option { - let mut format_args = None; - expr_visitor_no_bodies(|e| { - if format_args.is_some() { - return false; - } + for_each_expr(expr, |e| { let e_ctxt = e.span.ctxt(); if e_ctxt == expr.span.ctxt() { - return true; - } - if e_ctxt.outer_expn().is_descendant_of(expn_id) { - format_args = FormatArgsExpn::parse(cx, e); + ControlFlow::Continue(Descend::Yes) + } else if e_ctxt.outer_expn().is_descendant_of(expn_id) { + if let Some(args) = FormatArgsExpn::parse(cx, e) { + ControlFlow::Break(args) + } else { + ControlFlow::Continue(Descend::No) + } + } else { + ControlFlow::Continue(Descend::No) } - false }) - .visit_expr(expr); - format_args } /// Source callsite span of all inputs pub fn inputs_span(&self) -> Span { - match *self.value_args { + match *self.explicit_values { [] => self.format_string.span, [.., last] => self .format_string @@ -848,6 +967,22 @@ impl<'tcx> FormatArgsExpn<'tcx> { } } + /// Get the span of a value expanded to the previous comma, e.g. for the value `10` + /// + /// ```ignore + /// format("{}.{}", 10, 11) + /// // ^^^^ + /// ``` + pub fn value_with_prev_comma_span(&self, value_id: HirId) -> Option { + for (comma_span, value) in zip(&self.comma_spans, &self.explicit_values) { + if value.hir_id == value_id { + return Some(comma_span.to(hygiene::walk_chain(value.span, comma_span.ctxt()))); + } + } + + None + } + /// Iterator of all format params, both values and those referenced by `width`/`precision`s. pub fn params(&'tcx self) -> impl Iterator> { self.args diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 62020e21c815..8b843732a236 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -13,10 +13,11 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,62,0 { BOOL_THEN_SOME } + 1,58,0 { FORMAT_ARGS_CAPTURE } 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, UNSIGNED_ABS } - 1,50,0 { BOOL_THEN } + 1,50,0 { BOOL_THEN, CLAMP } 1,47,0 { TAU } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 07170e2df12a..13938645fc3e 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -34,7 +34,6 @@ pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "defa pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; /// Preferably use the diagnostic item `sym::deref_method` where possible pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; -pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"]; pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; #[cfg(feature = "internal")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; @@ -64,8 +63,6 @@ pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; -pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; -pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs index 0226f74906b5..88837d8a143e 100644 --- a/clippy_utils/src/ptr.rs +++ b/clippy_utils/src/ptr.rs @@ -1,7 +1,7 @@ use crate::source::snippet; -use crate::visitors::expr_visitor_no_bodies; +use crate::visitors::{for_each_expr, Descend}; use crate::{path_to_local_id, strip_pat_refs}; -use rustc_hir::intravisit::Visitor; +use core::ops::ControlFlow; use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -30,28 +30,23 @@ fn extract_clone_suggestions<'tcx>( replace: &[(&'static str, &'static str)], body: &'tcx Body<'_>, ) -> Option)>> { - let mut abort = false; let mut spans = Vec::new(); - expr_visitor_no_bodies(|expr| { - if abort { - return false; - } - if let ExprKind::MethodCall(seg, recv, [], _) = expr.kind { - if path_to_local_id(recv, id) { - if seg.ident.name.as_str() == "capacity" { - abort = true; - return false; - } - for &(fn_name, suffix) in replace { - if seg.ident.name.as_str() == fn_name { - spans.push((expr.span, snippet(cx, recv.span, "_") + suffix)); - return false; - } + for_each_expr(body, |e| { + if let ExprKind::MethodCall(seg, recv, [], _) = e.kind + && path_to_local_id(recv, id) + { + if seg.ident.as_str() == "capacity" { + return ControlFlow::Break(()); + } + for &(fn_name, suffix) in replace { + if seg.ident.as_str() == fn_name { + spans.push((e.span, snippet(cx, recv.span, "_") + suffix)); + return ControlFlow::Continue(Descend::No); } } } - !abort + ControlFlow::Continue(Descend::Yes) }) - .visit_body(body); - if abort { None } else { Some(spans) } + .is_none() + .then_some(spans) } diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index f7ce71917726..5a0721486e33 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -33,10 +33,10 @@ pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Trait(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, - ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), - ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), - ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), - ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate), + ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"), + ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"), + ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"), + ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {predicate:#?}"), } } match predicates.parent { @@ -319,8 +319,7 @@ fn check_terminator<'a, 'tcx>( span, format!( "can only call other `const fn` within a `const fn`, \ - but `{:?}` is not stable as `const fn`", - func, + but `{func:?}` is not stable as `const fn`", ) .into(), )); @@ -368,8 +367,9 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bo // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. - // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver` doesn't accept - // the `-dev` version number so we have to strip it off. + // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver` + // doesn't accept the `-dev` version number so we have to strip it + // off. let short_version = since .as_str() .split('-') @@ -380,8 +380,9 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bo crate::meets_msrv( msrv, - RustcVersion::parse(since.as_str()) - .unwrap_or_else(|err| panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")), + RustcVersion::parse(since.as_str()).unwrap_or_else(|err| { + panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}") + }), ) } else { // Unstable const fn with the feature enabled. diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index d85f591fb9a4..d28bd92d708b 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -25,11 +25,11 @@ pub fn expr_block<'a, T: LintContext>( if expr.span.from_expansion() { Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default))) } else if let ExprKind::Block(_, _) = expr.kind { - Cow::Owned(format!("{}{}", code, string)) + Cow::Owned(format!("{code}{string}")) } else if string.is_empty() { - Cow::Owned(format!("{{ {} }}", code)) + Cow::Owned(format!("{{ {code} }}")) } else { - Cow::Owned(format!("{{\n{};\n{}\n}}", code, string)) + Cow::Owned(format!("{{\n{code};\n{string}\n}}")) } } @@ -392,6 +392,16 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span { .span() } +/// Expand a span to include a preceding comma +/// ```rust,ignore +/// writeln!(o, "") -> writeln!(o, "") +/// ^^ ^^^^ +/// ``` +pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span { + let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true); + extended.with_lo(extended.lo() - BytePos(1)) +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; @@ -466,7 +476,7 @@ mod test { #[test] fn test_without_block_comments_lines_without_block_comments() { let result = without_block_comments(vec!["/*", "", "*/"]); - println!("result: {:?}", result); + println!("result: {result:?}"); assert!(result.is_empty()); let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index e53c40e95760..ef836e84829b 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -10,13 +10,13 @@ use rustc_ast_pretty::pprust::token_kind_to_string; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty; use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::borrow::Cow; use std::fmt::{Display, Write as _}; use std::ops::{Add, Neg, Not, Sub}; @@ -310,19 +310,19 @@ impl<'a> Sugg<'a> { /// Convenience method to transform suggestion into a return call pub fn make_return(self) -> Sugg<'static> { - Sugg::NonParen(Cow::Owned(format!("return {}", self))) + Sugg::NonParen(Cow::Owned(format!("return {self}"))) } /// Convenience method to transform suggestion into a block /// where the suggestion is a trailing expression pub fn blockify(self) -> Sugg<'static> { - Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self))) + Sugg::NonParen(Cow::Owned(format!("{{ {self} }}"))) } /// Convenience method to prefix the expression with the `async` keyword. /// Can be used after `blockify` to create an async block. pub fn asyncify(self) -> Sugg<'static> { - Sugg::NonParen(Cow::Owned(format!("async {}", self))) + Sugg::NonParen(Cow::Owned(format!("async {self}"))) } /// Convenience method to create the `..` or `...` @@ -346,12 +346,12 @@ impl<'a> Sugg<'a> { if has_enclosing_paren(&sugg) { Sugg::MaybeParen(sugg) } else { - Sugg::NonParen(format!("({})", sugg).into()) + Sugg::NonParen(format!("({sugg})").into()) } }, Sugg::BinOp(op, lhs, rhs) => { let sugg = binop_to_string(op, &lhs, &rhs); - Sugg::NonParen(format!("({})", sugg).into()) + Sugg::NonParen(format!("({sugg})").into()) }, } } @@ -379,20 +379,18 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { | AssocOp::Greater | AssocOp::GreaterEqual => { format!( - "{} {} {}", - lhs, - op.to_ast_binop().expect("Those are AST ops").to_string(), - rhs + "{lhs} {} {rhs}", + op.to_ast_binop().expect("Those are AST ops").to_string() ) }, - AssocOp::Assign => format!("{} = {}", lhs, rhs), + AssocOp::Assign => format!("{lhs} = {rhs}"), AssocOp::AssignOp(op) => { - format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs) + format!("{lhs} {}= {rhs}", token_kind_to_string(&token::BinOp(op))) }, - AssocOp::As => format!("{} as {}", lhs, rhs), - AssocOp::DotDot => format!("{}..{}", lhs, rhs), - AssocOp::DotDotEq => format!("{}..={}", lhs, rhs), - AssocOp::Colon => format!("{}: {}", lhs, rhs), + AssocOp::As => format!("{lhs} as {rhs}"), + AssocOp::DotDot => format!("{lhs}..{rhs}"), + AssocOp::DotDotEq => format!("{lhs}..={rhs}"), + AssocOp::Colon => format!("{lhs}: {rhs}"), } } @@ -523,7 +521,7 @@ impl Display for ParenHelper { /// operators have the same /// precedence. pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> { - Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into()) + Sugg::MaybeParen(format!("{op}{}", expr.maybe_par()).into()) } /// Builds the string for ` ` adding parenthesis when necessary. @@ -744,7 +742,7 @@ impl DiagnosticExt for rustc_errors::Diagnostic { if let Some(indent) = indentation(cx, item) { let span = item.with_hi(item.lo()); - self.span_suggestion(span, msg, format!("{}\n{}", attr, indent), applicability); + self.span_suggestion(span, msg, format!("{attr}\n{indent}"), applicability); } } @@ -758,14 +756,14 @@ impl DiagnosticExt for rustc_errors::Diagnostic { .map(|l| { if first { first = false; - format!("{}\n", l) + format!("{l}\n") } else { - format!("{}{}\n", indent, l) + format!("{indent}{l}\n") } }) .collect::(); - self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent), applicability); + self.span_suggestion(span, msg, format!("{new_item}\n{indent}"), applicability); } } @@ -863,7 +861,7 @@ impl<'tcx> DerefDelegate<'_, 'tcx> { pub fn finish(&mut self) -> String { let end_span = Span::new(self.next_pos, self.closure_span.hi(), self.closure_span.ctxt(), None); let end_snip = snippet_with_applicability(self.cx, end_span, "..", &mut self.applicability); - let sugg = format!("{}{}", self.suggestion_start, end_snip); + let sugg = format!("{}{end_snip}", self.suggestion_start); if self.closure_arg_is_type_annotated_double_ref { sugg.replacen('&', "", 1) } else { @@ -925,7 +923,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { if cmt.place.projections.is_empty() { // handle item without any projection, that needs an explicit borrowing // i.e.: suggest `&x` instead of `x` - let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str); + let _ = write!(self.suggestion_start, "{start_snip}&{ident_str}"); } else { // cases where a parent `Call` or `MethodCall` is using the item // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()` @@ -940,7 +938,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // given expression is the self argument and will be handled completely by the compiler // i.e.: `|x| x.is_something()` ExprKind::MethodCall(_, self_expr, ..) if self_expr.hir_id == cmt.hir_id => { - let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj); + let _ = write!(self.suggestion_start, "{start_snip}{ident_str_with_proj}"); self.next_pos = span.hi(); return; }, @@ -973,9 +971,9 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { } else { ident_str }; - format!("{}{}", start_snip, ident) + format!("{start_snip}{ident}") } else { - format!("{}&{}", start_snip, ident_str) + format!("{start_snip}&{ident_str}") }; self.suggestion_start.push_str(&ident_sugg); self.next_pos = span.hi(); @@ -1042,13 +1040,13 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { for item in projections { if item.kind == ProjectionKind::Deref { - replacement_str = format!("*{}", replacement_str); + replacement_str = format!("*{replacement_str}"); } } } } - let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str); + let _ = write!(self.suggestion_start, "{start_snip}{replacement_str}"); } self.next_pos = span.hi(); } @@ -1056,7 +1054,13 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } #[cfg(test)] diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 56343880320d..934470bd135b 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -12,11 +12,11 @@ use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; -use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_middle::ty::{ self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; +use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Size, VariantIdx}; diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 76bfec75726d..b5ec3fef3e0b 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,15 +1,16 @@ use crate as utils; -use crate::visitors::{expr_visitor, expr_visitor_no_bodies}; +use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend}; +use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; use rustc_hir::{Expr, ExprKind, HirId, Node}; +use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined. pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option { @@ -73,7 +74,13 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { self.update(cmt); } - fn fake_read(&mut self, _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} + fn fake_read( + &mut self, + _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: FakeReadCause, + _: HirId, + ) { + } } pub struct ParamBindingIdCollector { @@ -142,28 +149,17 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { } pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { - let mut seen_return_break_continue = false; - expr_visitor_no_bodies(|ex| { - if seen_return_break_continue { - return false; - } - match &ex.kind { - ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { - seen_return_break_continue = true; - }, + for_each_expr(expression, |e| { + match e.kind { + ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), // Something special could be done here to handle while or for loop // desugaring, as this will detect a break if there's a while loop // or a for loop inside the expression. - _ => { - if ex.span.from_expansion() { - seen_return_break_continue = true; - } - }, + _ if e.span.from_expansion() => ControlFlow::Break(()), + _ => ControlFlow::Continue(()), } - !seen_return_break_continue }) - .visit_expr(expression); - seen_return_break_continue + .is_some() } pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool { @@ -194,23 +190,16 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr return true; } - let mut used_after_expr = false; let mut past_expr = false; - expr_visitor(cx, |expr| { - if used_after_expr { - return false; - } - - if expr.hir_id == after.hir_id { + for_each_expr_with_closures(cx, block, |e| { + if e.hir_id == after.hir_id { past_expr = true; - return false; - } - - if past_expr && utils::path_to_local_id(expr, local_id) { - used_after_expr = true; + ControlFlow::Continue(Descend::No) + } else if past_expr && utils::path_to_local_id(e, local_id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::Yes) } - !used_after_expr }) - .visit_block(block); - used_after_expr + .is_some() } diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 232d571902b6..d4294f18fd50 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -5,14 +5,13 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; use rustc_hir::{ - Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, Stmt, UnOp, - UnsafeSource, Unsafety, + AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, + Stmt, UnOp, UnsafeSource, Unsafety, }; use rustc_lint::LateContext; -use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, Ty, TypeckResults}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults}; use rustc_span::Span; mod internal { @@ -48,6 +47,26 @@ impl Continue for Descend { } } +/// A type which can be visited. +pub trait Visitable<'tcx> { + /// Calls the corresponding `visit_*` function on the visitor. + fn visit>(self, visitor: &mut V); +} +macro_rules! visitable_ref { + ($t:ident, $f:ident) => { + impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { + fn visit>(self, visitor: &mut V) { + visitor.$f(self); + } + } + }; +} +visitable_ref!(Arm, visit_arm); +visitable_ref!(Block, visit_block); +visitable_ref!(Body, visit_body); +visitable_ref!(Expr, visit_expr); +visitable_ref!(Stmt, visit_stmt); + /// Calls the given function once for each expression contained. This does not enter any bodies or /// nested items. pub fn for_each_expr<'tcx, B, C: Continue>( @@ -82,57 +101,63 @@ pub fn for_each_expr<'tcx, B, C: Continue>( v.res } -/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested -/// bodies (i.e. closures) are visited. -/// If the callback returns `true`, the expr just provided to the callback is walked. -#[must_use] -pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> { - struct V<'tcx, F> { - hir: Map<'tcx>, +/// Calls the given function once for each expression contained. This will enter bodies, but not +/// nested items. +pub fn for_each_expr_with_closures<'tcx, B, C: Continue>( + cx: &LateContext<'tcx>, + node: impl Visitable<'tcx>, + f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, +) -> Option { + struct V<'tcx, B, F> { + tcx: TyCtxt<'tcx>, f: F, + res: Option, } - impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> { + impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow> Visitor<'tcx> for V<'tcx, B, F> { type NestedFilter = nested_filter::OnlyBodies; fn nested_visit_map(&mut self) -> Self::Map { - self.hir + self.tcx.hir() } - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if (self.f)(expr) { - walk_expr(self, expr); + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + if self.res.is_some() { + return; } - } - } - V { hir: cx.tcx.hir(), f } -} - -/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested -/// bodies (i.e. closures) are not visited. -/// If the callback returns `true`, the expr just provided to the callback is walked. -#[must_use] -pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> { - struct V(F); - impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V { - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - if (self.0)(e) { - walk_expr(self, e); + match (self.f)(e) { + ControlFlow::Continue(c) if c.descend() => walk_expr(self, e), + ControlFlow::Break(b) => self.res = Some(b), + ControlFlow::Continue(_) => (), } } + + // Only walk closures + fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + // Avoid unnecessary `walk_*` calls. + fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {} + fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {} + fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {} + // Avoid monomorphising all `visit_*` functions. + fn visit_nested_item(&mut self, _: ItemId) {} } - V(f) + let mut v = V { + tcx: cx.tcx, + f, + res: None, + }; + node.visit(&mut v); + v.res } /// returns `true` if expr contains match expr desugared from try fn contains_try(expr: &hir::Expr<'_>) -> bool { - let mut found = false; - expr_visitor_no_bodies(|e| { - if !found { - found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)); + for_each_expr(expr, |e| { + if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - !found }) - .visit_expr(expr); - found + .is_some() } pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool @@ -228,68 +253,29 @@ where } } -/// A type which can be visited. -pub trait Visitable<'tcx> { - /// Calls the corresponding `visit_*` function on the visitor. - fn visit>(self, visitor: &mut V); -} -macro_rules! visitable_ref { - ($t:ident, $f:ident) => { - impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> { - fn visit>(self, visitor: &mut V) { - visitor.$f(self); - } - } - }; -} -visitable_ref!(Arm, visit_arm); -visitable_ref!(Block, visit_block); -visitable_ref!(Body, visit_body); -visitable_ref!(Expr, visit_expr); -visitable_ref!(Stmt, visit_stmt); - -// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I -// where -// I::Item: Visitable<'tcx>, -// { -// fn visit>(self, visitor: &mut V) { -// for x in self { -// x.visit(visitor); -// } -// } -// } - /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { - let mut found = false; - expr_visitor(cx, |e| { - if found { - return false; - } - + for_each_expr_with_closures(cx, cx.tcx.hir().body(body).value, |e| { if let ExprKind::Path(p) = &e.kind { if cx.qpath_res(p, e.hir_id) == res { - found = true; + return ControlFlow::Break(()); } } - !found + ControlFlow::Continue(()) }) - .visit_expr(cx.tcx.hir().body(body).value); - found + .is_some() } /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { - let mut is_used = false; - let mut visitor = expr_visitor(cx, |expr| { - if !is_used { - is_used = path_to_local_id(expr, id); + for_each_expr_with_closures(cx, visitable, |e| { + if path_to_local_id(e, id) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) } - !is_used - }); - visitable.visit(&mut visitor); - drop(visitor); - is_used + }) + .is_some() } /// Checks if the given expression is a constant. diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 737c845c0451..de31c16b819e 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -12,9 +12,11 @@ publish = false [dependencies] cargo_metadata = "0.14" clap = "3.2" +crossbeam-channel = "0.5.6" flate2 = "1.0" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.85" tar = "0.4" toml = "0.5" ureq = "2.2" diff --git a/lintcheck/README.md b/lintcheck/README.md index 6f3d23382ce1..6142de5e3130 100644 --- a/lintcheck/README.md +++ b/lintcheck/README.md @@ -69,9 +69,27 @@ is checked. is explicitly specified in the options. ### Fix mode -You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `--fix` and +You can run `cargo lintcheck --fix` which will run Clippy with `--fix` and print a warning if Clippy's suggestions fail to apply (if the resulting code does not build). This lets us spot bad suggestions or false positives automatically in some cases. Please note that the target dir should be cleaned afterwards since clippy will modify the downloaded sources which can lead to unexpected results when running lintcheck again afterwards. + +### Recursive mode +You can run `cargo lintcheck --recursive` to also run Clippy on the dependencies +of the crates listed in the crates source `.toml`. e.g. adding `rand 0.8.5` +would also lint `rand_core`, `rand_chacha`, etc. + +Particularly slow crates in the dependency graph can be ignored using +`recursive.ignore`: + +```toml +[crates] +cargo = {name = "cargo", versions = ['0.64.0']} + +[recursive] +ignore = [ + "unicode-normalization", +] +``` diff --git a/lintcheck/lintcheck_crates.toml b/lintcheck/lintcheck_crates.toml index ebbe9c9ae675..52f7fee47b61 100644 --- a/lintcheck/lintcheck_crates.toml +++ b/lintcheck/lintcheck_crates.toml @@ -33,3 +33,11 @@ cfg-expr = {name = "cfg-expr", versions = ['0.7.1']} puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"} rpmalloc = {name = "rpmalloc", versions = ['0.2.0']} tame-oidc = {name = "tame-oidc", versions = ['0.1.0']} + +[recursive] +ignore = [ + # Takes ~30s to lint + "combine", + # Has 1.2 million `clippy::match_same_arms`s + "unicode-normalization", +] diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 1742cf677c0f..b344db634f61 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -34,11 +34,16 @@ fn get_clap_config() -> ArgMatches { Arg::new("markdown") .long("markdown") .help("Change the reports table to use markdown links"), + Arg::new("recursive") + .long("--recursive") + .help("Run clippy on the dependencies of crates specified in crates-toml") + .conflicts_with("threads") + .conflicts_with("fix"), ]) .get_matches() } -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct LintcheckConfig { /// max number of jobs to spawn (default 1) pub max_jobs: usize, @@ -54,6 +59,8 @@ pub(crate) struct LintcheckConfig { pub lint_filter: Vec, /// Indicate if the output should support markdown syntax pub markdown: bool, + /// Run clippy on the dependencies of crates + pub recursive: bool, } impl LintcheckConfig { @@ -119,6 +126,7 @@ impl LintcheckConfig { fix: clap_config.contains_id("fix"), lint_filter, markdown, + recursive: clap_config.contains_id("recursive"), } } } diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs new file mode 100644 index 000000000000..63221bab32d3 --- /dev/null +++ b/lintcheck/src/driver.rs @@ -0,0 +1,67 @@ +use crate::recursive::{deserialize_line, serialize_line, DriverInfo}; + +use std::io::{self, BufReader, Write}; +use std::net::TcpStream; +use std::process::{self, Command, Stdio}; +use std::{env, mem}; + +/// 1. Sends [DriverInfo] to the [crate::recursive::LintcheckServer] running on `addr` +/// 2. Receives [bool] from the server, if `false` returns `None` +/// 3. Otherwise sends the stderr of running `clippy-driver` to the server +fn run_clippy(addr: &str) -> Option { + let driver_info = DriverInfo { + package_name: env::var("CARGO_PKG_NAME").ok()?, + crate_name: env::var("CARGO_CRATE_NAME").ok()?, + version: env::var("CARGO_PKG_VERSION").ok()?, + }; + + let mut stream = BufReader::new(TcpStream::connect(addr).unwrap()); + + serialize_line(&driver_info, stream.get_mut()); + + let should_run = deserialize_line::(&mut stream); + if !should_run { + return None; + } + + // Remove --cap-lints allow so that clippy runs and lints are emitted + let mut include_next = true; + let args = env::args().skip(1).filter(|arg| match arg.as_str() { + "--cap-lints=allow" => false, + "--cap-lints" => { + include_next = false; + false + }, + _ => mem::replace(&mut include_next, true), + }); + + let output = Command::new(env::var("CLIPPY_DRIVER").expect("missing env CLIPPY_DRIVER")) + .args(args) + .stdout(Stdio::inherit()) + .output() + .expect("failed to run clippy-driver"); + + stream + .get_mut() + .write_all(&output.stderr) + .unwrap_or_else(|e| panic!("{e:?} in {driver_info:?}")); + + match output.status.code() { + Some(0) => Some(0), + code => { + io::stderr().write_all(&output.stderr).unwrap(); + Some(code.expect("killed by signal")) + }, + } +} + +pub fn drive(addr: &str) { + process::exit(run_clippy(addr).unwrap_or_else(|| { + Command::new("rustc") + .args(env::args_os().skip(2)) + .status() + .unwrap() + .code() + .unwrap() + })) +} diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 9ee25280f046..cc2b3e1acec7 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -8,13 +8,17 @@ #![allow(clippy::collapsible_else_if)] mod config; +mod driver; +mod recursive; -use config::LintcheckConfig; +use crate::config::LintcheckConfig; +use crate::recursive::LintcheckServer; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::env; +use std::env::consts::EXE_SUFFIX; use std::fmt::Write as _; -use std::fs::write; +use std::fs; use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::process::Command; @@ -22,22 +26,12 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; use std::time::Duration; -use cargo_metadata::diagnostic::DiagnosticLevel; +use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel}; use cargo_metadata::Message; use rayon::prelude::*; use serde::{Deserialize, Serialize}; use walkdir::{DirEntry, WalkDir}; -#[cfg(not(windows))] -const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver"; -#[cfg(not(windows))] -const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy"; - -#[cfg(windows)] -const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe"; -#[cfg(windows)] -const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe"; - const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads"; const LINTCHECK_SOURCES: &str = "target/lintcheck/sources"; @@ -45,6 +39,13 @@ const LINTCHECK_SOURCES: &str = "target/lintcheck/sources"; #[derive(Debug, Serialize, Deserialize)] struct SourceList { crates: HashMap, + #[serde(default)] + recursive: RecursiveOptions, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +struct RecursiveOptions { + ignore: HashSet, } /// A crate source stored inside the .toml @@ -105,12 +106,7 @@ struct ClippyWarning { #[allow(unused)] impl ClippyWarning { - fn new(cargo_message: Message, krate: &Crate) -> Option { - let diag = match cargo_message { - Message::CompilerMessage(message) => message.message, - _ => return None, - }; - + fn new(diag: Diagnostic, crate_name: &str, crate_version: &str) -> Option { let lint_type = diag.code?.code; if !(lint_type.contains("clippy") || diag.message.contains("clippy")) || diag.message.contains("could not read cargo metadata") @@ -124,12 +120,12 @@ impl ClippyWarning { Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), Err(_) => format!( "target/lintcheck/sources/{}-{}/{}", - krate.name, krate.version, span.file_name + crate_name, crate_version, span.file_name ), }; Some(Self { - crate_name: krate.name.clone(), + crate_name: crate_name.to_owned(), file, line: span.line_start, column: span.column_start, @@ -142,8 +138,6 @@ impl ClippyWarning { fn to_output(&self, markdown: bool) -> String { let file_with_pos = format!("{}:{}:{}", &self.file, &self.line, &self.column); if markdown { - let lint = format!("`{}`", self.lint_type); - let mut file = self.file.clone(); if !file.starts_with('$') { file.insert_str(0, "../"); @@ -151,7 +145,7 @@ impl ClippyWarning { let mut output = String::from("| "); let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); - let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message); + let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); output.push('\n'); output } else { @@ -243,6 +237,7 @@ impl CrateSource { } // check out the commit/branch/whatever if !Command::new("git") + .args(["-c", "advice.detachedHead=false"]) .arg("checkout") .arg(commit) .current_dir(&repo_path) @@ -309,10 +304,12 @@ impl Crate { fn run_clippy_lints( &self, cargo_clippy_path: &Path, + clippy_driver_path: &Path, target_dir_index: &AtomicUsize, total_crates_to_lint: usize, config: &LintcheckConfig, lint_filter: &Vec, + server: &Option, ) -> Vec { // advance the atomic index by one let index = target_dir_index.fetch_add(1, Ordering::SeqCst); @@ -336,36 +333,67 @@ impl Crate { let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); - let mut args = if config.fix { + let mut cargo_clippy_args = if config.fix { vec!["--fix", "--"] } else { vec!["--", "--message-format=json", "--"] }; + let mut clippy_args = Vec::<&str>::new(); if let Some(options) = &self.options { for opt in options { - args.push(opt); + clippy_args.push(opt); } } else { - args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) + clippy_args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) } if lint_filter.is_empty() { - args.push("--cap-lints=warn"); + clippy_args.push("--cap-lints=warn"); } else { - args.push("--cap-lints=allow"); - args.extend(lint_filter.iter().map(|filter| filter.as_str())) + clippy_args.push("--cap-lints=allow"); + clippy_args.extend(lint_filter.iter().map(|filter| filter.as_str())) + } + + if let Some(server) = server { + let target = shared_target_dir.join("recursive"); + + // `cargo clippy` is a wrapper around `cargo check` that mainly sets `RUSTC_WORKSPACE_WRAPPER` to + // `clippy-driver`. We do the same thing here with a couple changes: + // + // `RUSTC_WRAPPER` is used instead of `RUSTC_WORKSPACE_WRAPPER` so that we can lint all crate + // dependencies rather than only workspace members + // + // The wrapper is set to the `lintcheck` so we can force enable linting and ignore certain crates + // (see `crate::driver`) + let status = Command::new("cargo") + .arg("check") + .arg("--quiet") + .current_dir(&self.path) + .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__")) + .env("CARGO_TARGET_DIR", target) + .env("RUSTC_WRAPPER", env::current_exe().unwrap()) + // Pass the absolute path so `crate::driver` can find `clippy-driver`, as it's executed in various + // different working directories + .env("CLIPPY_DRIVER", clippy_driver_path) + .env("LINTCHECK_SERVER", server.local_addr.to_string()) + .status() + .expect("failed to run cargo"); + + assert_eq!(status.code(), Some(0)); + + return Vec::new(); } - let all_output = std::process::Command::new(&cargo_clippy_path) + cargo_clippy_args.extend(clippy_args); + + let all_output = Command::new(&cargo_clippy_path) // use the looping index to create individual target dirs .env( "CARGO_TARGET_DIR", shared_target_dir.join(format!("_{:?}", thread_index)), ) - // lint warnings will look like this: - // src/cargo/ops/cargo_compile.rs:127:35: warning: usage of `FromIterator::from_iter` - .args(&args) + .args(&cargo_clippy_args) .current_dir(&self.path) .output() .unwrap_or_else(|error| { @@ -404,7 +432,10 @@ impl Crate { // get all clippy warnings and ICEs let warnings: Vec = Message::parse_stream(stdout.as_bytes()) - .filter_map(|msg| ClippyWarning::new(msg.unwrap(), &self)) + .filter_map(|msg| match msg { + Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version), + _ => None, + }) .collect(); warnings @@ -423,8 +454,8 @@ fn build_clippy() { } } -/// Read a `toml` file and return a list of `CrateSources` that we want to check with clippy -fn read_crates(toml_path: &Path) -> Vec { +/// Read a `lintcheck_crates.toml` file +fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = @@ -484,7 +515,7 @@ fn read_crates(toml_path: &Path) -> Vec { // sort the crates crate_sources.sort(); - crate_sources + (crate_sources, crate_list.recursive) } /// Generate a short list of occurring lints-types and their count @@ -516,20 +547,20 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, /// check if the latest modification of the logfile is older than the modification date of the /// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck -fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { +fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool { if !lintcheck_logs_path.exists() { return true; } let clippy_modified: std::time::SystemTime = { - let mut times = [CLIPPY_DRIVER_PATH, CARGO_CLIPPY_PATH].iter().map(|p| { + let [cargo, driver] = paths.map(|p| { std::fs::metadata(p) .expect("failed to get metadata of file") .modified() .expect("failed to get modification date") }); // the oldest modification of either of the binaries - std::cmp::max(times.next().unwrap(), times.next().unwrap()) + std::cmp::max(cargo, driver) }; let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path) @@ -543,6 +574,11 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { } fn main() { + // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` + if let Ok(addr) = env::var("LINTCHECK_SERVER") { + driver::drive(&addr); + } + // assert that we launch lintcheck from the repo root (via cargo lintcheck) if std::fs::metadata("lintcheck/Cargo.toml").is_err() { eprintln!("lintcheck needs to be run from clippy's repo root!\nUse `cargo lintcheck` alternatively."); @@ -555,9 +591,15 @@ fn main() { build_clippy(); println!("Done compiling"); + let cargo_clippy_path = fs::canonicalize(format!("target/debug/cargo-clippy{EXE_SUFFIX}")).unwrap(); + let clippy_driver_path = fs::canonicalize(format!("target/debug/clippy-driver{EXE_SUFFIX}")).unwrap(); + // if the clippy bin is newer than our logs, throw away target dirs to force clippy to // refresh the logs - if lintcheck_needs_rerun(&config.lintcheck_results_path) { + if lintcheck_needs_rerun( + &config.lintcheck_results_path, + [&cargo_clippy_path, &clippy_driver_path], + ) { let shared_target_dir = "target/lintcheck/shared_target_dir"; // if we get an Err here, the shared target dir probably does simply not exist if let Ok(metadata) = std::fs::metadata(&shared_target_dir) { @@ -569,10 +611,6 @@ fn main() { } } - let cargo_clippy_path: PathBuf = PathBuf::from(CARGO_CLIPPY_PATH) - .canonicalize() - .expect("failed to canonicalize path to clippy binary"); - // assert that clippy is found assert!( cargo_clippy_path.is_file(), @@ -580,7 +618,7 @@ fn main() { cargo_clippy_path.display() ); - let clippy_ver = std::process::Command::new(CARGO_CLIPPY_PATH) + let clippy_ver = std::process::Command::new(&cargo_clippy_path) .arg("--version") .output() .map(|o| String::from_utf8_lossy(&o.stdout).into_owned()) @@ -589,7 +627,7 @@ fn main() { // download and extract the crates, then run clippy on them and collect clippy's warnings // flatten into one big list of warnings - let crates = read_crates(&config.sources_toml_path); + let (crates, recursive_options) = read_crates(&config.sources_toml_path); let old_stats = read_stats_from_file(&config.lintcheck_results_path); let counter = AtomicUsize::new(1); @@ -639,11 +677,31 @@ fn main() { .build_global() .unwrap(); - let clippy_warnings: Vec = crates + let server = config.recursive.then(|| { + let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); + + LintcheckServer::spawn(recursive_options) + }); + + let mut clippy_warnings: Vec = crates .par_iter() - .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, crates.len(), &config, &lint_filter)) + .flat_map(|krate| { + krate.run_clippy_lints( + &cargo_clippy_path, + &clippy_driver_path, + &counter, + crates.len(), + &config, + &lint_filter, + &server, + ) + }) .collect(); + if let Some(server) = server { + clippy_warnings.extend(server.warnings()); + } + // if we are in --fix mode, don't change the log files, terminate here if config.fix { return; @@ -681,8 +739,8 @@ fn main() { } println!("Writing logs to {}", config.lintcheck_results_path.display()); - std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap(); - write(&config.lintcheck_results_path, text).unwrap(); + fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap(); + fs::write(&config.lintcheck_results_path, text).unwrap(); print_stats(old_stats, new_stats, &config.lint_filter); } diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs new file mode 100644 index 000000000000..67dcfc2b199c --- /dev/null +++ b/lintcheck/src/recursive.rs @@ -0,0 +1,123 @@ +//! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`, +//! this allows [crate::driver] to be run for every dependency. The driver connects to +//! [LintcheckServer] to ask if it should be skipped, and if not sends the stderr of running clippy +//! on the crate to the server + +use crate::ClippyWarning; +use crate::RecursiveOptions; + +use std::collections::HashSet; +use std::io::{BufRead, BufReader, Read, Write}; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::sync::{Arc, Mutex}; +use std::thread; + +use cargo_metadata::diagnostic::Diagnostic; +use crossbeam_channel::{Receiver, Sender}; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)] +pub(crate) struct DriverInfo { + pub package_name: String, + pub crate_name: String, + pub version: String, +} + +pub(crate) fn serialize_line(value: &T, writer: &mut W) +where + T: Serialize, + W: Write, +{ + let mut buf = serde_json::to_vec(&value).expect("failed to serialize"); + buf.push(b'\n'); + writer.write_all(&buf).expect("write_all failed"); +} + +pub(crate) fn deserialize_line(reader: &mut R) -> T +where + T: DeserializeOwned, + R: BufRead, +{ + let mut string = String::new(); + reader.read_line(&mut string).expect("read_line failed"); + serde_json::from_str(&string).expect("failed to deserialize") +} + +fn process_stream( + stream: TcpStream, + sender: &Sender, + options: &RecursiveOptions, + seen: &Mutex>, +) { + let mut stream = BufReader::new(stream); + + let driver_info: DriverInfo = deserialize_line(&mut stream); + + let unseen = seen.lock().unwrap().insert(driver_info.clone()); + let ignored = options.ignore.contains(&driver_info.package_name); + let should_run = unseen && !ignored; + + serialize_line(&should_run, stream.get_mut()); + + let mut stderr = String::new(); + stream.read_to_string(&mut stderr).unwrap(); + + let messages = stderr + .lines() + .filter_map(|json_msg| serde_json::from_str::(json_msg).ok()) + .filter_map(|diag| ClippyWarning::new(diag, &driver_info.package_name, &driver_info.version)); + + for message in messages { + sender.send(message).unwrap(); + } +} + +pub(crate) struct LintcheckServer { + pub local_addr: SocketAddr, + receiver: Receiver, + sender: Arc>, +} + +impl LintcheckServer { + pub fn spawn(options: RecursiveOptions) -> Self { + let listener = TcpListener::bind("localhost:0").unwrap(); + let local_addr = listener.local_addr().unwrap(); + + let (sender, receiver) = crossbeam_channel::unbounded::(); + let sender = Arc::new(sender); + // The spawned threads hold a `Weak` so that they don't keep the channel connected + // indefinitely + let sender_weak = Arc::downgrade(&sender); + + // Ignore dependencies multiple times, e.g. for when it's both checked and compiled for a + // build dependency + let seen = Mutex::default(); + + thread::spawn(move || { + thread::scope(|s| { + s.spawn(|| { + while let Ok((stream, _)) = listener.accept() { + let sender = sender_weak.upgrade().expect("received connection after server closed"); + let options = &options; + let seen = &seen; + s.spawn(move || process_stream(stream, &sender, options, seen)); + } + }); + }); + }); + + Self { + local_addr, + sender, + receiver, + } + } + + pub fn warnings(self) -> impl Iterator { + // causes the channel to become disconnected so that the receiver iterator ends + drop(self.sender); + + self.receiver.into_iter() + } +} diff --git a/rust-toolchain b/rust-toolchain index b6976366dafc..49b13cb54e71 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-09-08" +channel = "nightly-2022-10-06" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/rustc_tools_util/Cargo.toml b/rustc_tools_util/Cargo.toml index 9554d4d6c003..89c3d6aaa89e 100644 --- a/rustc_tools_util/Cargo.toml +++ b/rustc_tools_util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustc_tools_util" -version = "0.2.0" +version = "0.2.1" description = "small helper to generate version information for git packages" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index 01891b51d3b0..e947f9c7e66e 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -6,17 +6,17 @@ for packages installed from a git repo ## Usage Add a `build.rs` file to your repo and list it in `Cargo.toml` -```` +````toml build = "build.rs" ```` List rustc_tools_util as regular AND build dependency. -```` +````toml [dependencies] -rustc_tools_util = "0.1" +rustc_tools_util = "0.2.1" [build-dependencies] -rustc_tools_util = "0.1" +rustc_tools_util = "0.2.1" ```` In `build.rs`, generate the data in your `main()` diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index 429dddc42ea9..01d25c53126f 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -48,8 +48,8 @@ impl std::fmt::Display for VersionInfo { if (hash_trimmed.len() + date_trimmed.len()) > 0 { write!( f, - "{} {}.{}.{} ({} {})", - self.crate_name, self.major, self.minor, self.patch, hash_trimmed, date_trimmed, + "{} {}.{}.{} ({hash_trimmed} {date_trimmed})", + self.crate_name, self.major, self.minor, self.patch, )?; } else { write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?; @@ -137,7 +137,7 @@ mod test { let vi = get_version_info!(); assert_eq!(vi.major, 0); assert_eq!(vi.minor, 2); - assert_eq!(vi.patch, 0); + assert_eq!(vi.patch, 1); assert_eq!(vi.crate_name, "rustc_tools_util"); // hard to make positive tests for these since they will always change assert!(vi.commit_hash.is_none()); @@ -147,16 +147,16 @@ mod test { #[test] fn test_display_local() { let vi = get_version_info!(); - assert_eq!(vi.to_string(), "rustc_tools_util 0.2.0"); + assert_eq!(vi.to_string(), "rustc_tools_util 0.2.1"); } #[test] fn test_debug_local() { let vi = get_version_info!(); - let s = format!("{:?}", vi); + let s = format!("{vi:?}"); assert_eq!( s, - "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 0 }" + "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 1 }" ); } } diff --git a/src/docs.rs b/src/docs.rs index 6c89b4dde372..3bf488ab4779 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -48,6 +48,7 @@ docs! { "borrow_interior_mutable_const", "borrowed_box", "box_collection", + "box_default", "boxed_local", "branches_sharing_code", "builtin_type_shadow", @@ -105,6 +106,7 @@ docs! { "derive_hash_xor_eq", "derive_ord_xor_partial_ord", "derive_partial_eq_without_eq", + "disallowed_macros", "disallowed_methods", "disallowed_names", "disallowed_script_idents", @@ -190,6 +192,7 @@ docs! { "implicit_clone", "implicit_hasher", "implicit_return", + "implicit_saturating_add", "implicit_saturating_sub", "imprecise_flops", "inconsistent_digit_grouping", @@ -254,6 +257,7 @@ docs! { "manual_assert", "manual_async_fn", "manual_bits", + "manual_clamp", "manual_filter_map", "manual_find", "manual_find_map", @@ -521,6 +525,7 @@ docs! { "unimplemented", "uninit_assumed_init", "uninit_vec", + "uninlined_format_args", "unit_arg", "unit_cmp", "unit_hash", diff --git a/src/docs/arithmetic_side_effects.txt b/src/docs/arithmetic_side_effects.txt index 6c7d51a4989e..4ae8bce88ad5 100644 --- a/src/docs/arithmetic_side_effects.txt +++ b/src/docs/arithmetic_side_effects.txt @@ -5,7 +5,7 @@ Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing accordin Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), or can panic (`/`, `%`). -Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant +Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant environments, allowed types and non-constant operations that won't overflow are ignored. ### Why is this bad? diff --git a/src/docs/box_default.txt b/src/docs/box_default.txt new file mode 100644 index 000000000000..ffac894d0c50 --- /dev/null +++ b/src/docs/box_default.txt @@ -0,0 +1,23 @@ +### What it does +checks for `Box::new(T::default())`, which is better written as +`Box::::default()`. + +### Why is this bad? +First, it's more complex, involving two calls instead of one. +Second, `Box::default()` can be faster +[in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). + +### Known problems +The lint may miss some cases (e.g. Box::new(String::from(""))). +On the other hand, it will trigger on cases where the `default` +code comes from a macro that does something different based on +e.g. target operating system. + +### Example +``` +let x: Box = Box::new(Default::default()); +``` +Use instead: +``` +let x: Box = Box::default(); +``` \ No newline at end of file diff --git a/src/docs/disallowed_macros.txt b/src/docs/disallowed_macros.txt new file mode 100644 index 000000000000..96fa15afabfd --- /dev/null +++ b/src/docs/disallowed_macros.txt @@ -0,0 +1,36 @@ +### What it does +Denies the configured macros in clippy.toml + +Note: Even though this lint is warn-by-default, it will only trigger if +macros are defined in the clippy.toml file. + +### Why is this bad? +Some macros are undesirable in certain contexts, and it's beneficial to +lint for them as needed. + +### Example +An example clippy.toml configuration: +``` +disallowed-macros = [ + # Can use a string as the path of the disallowed macro. + "std::print", + # Can also use an inline table with a `path` key. + { path = "std::println" }, + # When using an inline table, can add a `reason` for why the macro + # is disallowed. + { path = "serde::Serialize", reason = "no serializing" }, +] +``` +``` +use serde::Serialize; + +// Example code where clippy issues a warning +println!("warns"); + +// The diagnostic will contain the message "no serializing" +#[derive(Serialize)] +struct Data { + name: String, + value: usize, +} +``` \ No newline at end of file diff --git a/src/docs/implicit_saturating_add.txt b/src/docs/implicit_saturating_add.txt new file mode 100644 index 000000000000..5883a5363e2b --- /dev/null +++ b/src/docs/implicit_saturating_add.txt @@ -0,0 +1,20 @@ +### What it does +Checks for implicit saturating addition. + +### Why is this bad? +The built-in function is more readable and may be faster. + +### Example +``` +let mut u:u32 = 7000; + +if u != u32::MAX { + u += 1; +} +``` +Use instead: +``` +let mut u:u32 = 7000; + +u = u.saturating_add(1); +``` \ No newline at end of file diff --git a/src/docs/manual_clamp.txt b/src/docs/manual_clamp.txt new file mode 100644 index 000000000000..8993f6683adf --- /dev/null +++ b/src/docs/manual_clamp.txt @@ -0,0 +1,46 @@ +### What it does +Identifies good opportunities for a clamp function from std or core, and suggests using it. + +### Why is this bad? +clamp is much shorter, easier to read, and doesn't use any control flow. + +### Known issue(s) +If the clamped variable is NaN this suggestion will cause the code to propagate NaN +rather than returning either `max` or `min`. + +`clamp` functions will panic if `max < min`, `max.is_nan()`, or `min.is_nan()`. +Some may consider panicking in these situations to be desirable, but it also may +introduce panicking where there wasn't any before. + +### Examples +``` +if input > max { + max +} else if input < min { + min +} else { + input +} +``` + +``` +input.max(min).min(max) +``` + +``` +match input { + x if x > max => max, + x if x < min => min, + x => x, +} +``` + +``` +let mut x = input; +if x < min { x = min; } +if x > max { x = max; } +``` +Use instead: +``` +input.clamp(min, max) +``` \ No newline at end of file diff --git a/src/docs/needless_borrowed_reference.txt b/src/docs/needless_borrowed_reference.txt index 55faa0cf5719..152459ba1c9d 100644 --- a/src/docs/needless_borrowed_reference.txt +++ b/src/docs/needless_borrowed_reference.txt @@ -1,30 +1,22 @@ ### What it does -Checks for bindings that destructure a reference and borrow the inner +Checks for bindings that needlessly destructure a reference and borrow the inner value with `&ref`. ### Why is this bad? This pattern has no effect in almost all cases. -### Known problems -In some cases, `&ref` is needed to avoid a lifetime mismatch error. -Example: -``` -fn foo(a: &Option, b: &Option) { - match (a, b) { - (None, &ref c) | (&ref c, None) => (), - (&Some(ref c), _) => (), - }; -} -``` - ### Example ``` let mut v = Vec::::new(); v.iter_mut().filter(|&ref a| a.is_empty()); + +if let &[ref first, ref second] = v.as_slice() {} ``` Use instead: ``` let mut v = Vec::::new(); v.iter_mut().filter(|a| a.is_empty()); + +if let [first, second] = v.as_slice() {} ``` \ No newline at end of file diff --git a/src/docs/similar_names.txt b/src/docs/similar_names.txt index 13aca9c0bb75..f9eff21b6793 100644 --- a/src/docs/similar_names.txt +++ b/src/docs/similar_names.txt @@ -1,6 +1,10 @@ ### What it does Checks for names that are very similar and thus confusing. +Note: this lint looks for similar names throughout each +scope. To allow it, you need to allow it on the scope +level, not on the name that is reported. + ### Why is this bad? It's hard to distinguish between names that differ only by a single character. diff --git a/src/docs/uninlined_format_args.txt b/src/docs/uninlined_format_args.txt new file mode 100644 index 000000000000..3d2966c84dbe --- /dev/null +++ b/src/docs/uninlined_format_args.txt @@ -0,0 +1,36 @@ +### What it does +Detect when a variable is not inlined in a format string, +and suggests to inline it. + +### Why is this bad? +Non-inlined code is slightly more difficult to read and understand, +as it requires arguments to be matched against the format string. +The inlined syntax, where allowed, is simpler. + +### Example +``` +format!("{}", var); +format!("{v:?}", v = var); +format!("{0} {0}", var); +format!("{0:1$}", var, width); +format!("{:.*}", prec, var); +``` +Use instead: +``` +format!("{var}"); +format!("{var:?}"); +format!("{var} {var}"); +format!("{var:width$}"); +format!("{var:.prec$}"); +``` + +### Known Problems + +There may be a false positive if the format string is expanded from certain proc macros: + +``` +println!(indoc!("{}"), var); +``` + +If a format string contains a numbered argument that cannot be inlined +nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. \ No newline at end of file diff --git a/src/driver.rs b/src/driver.rs index 235eae5af1ec..b12208ac62a8 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -193,8 +193,8 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { let xs: Vec> = vec![ "the compiler unexpectedly panicked. this is a bug.".into(), - format!("we would appreciate a bug report: {}", bug_report_url).into(), - format!("Clippy version: {}", version_info).into(), + format!("we would appreciate a bug report: {bug_report_url}").into(), + format!("Clippy version: {version_info}").into(), ]; for note in &xs { @@ -290,7 +290,7 @@ pub fn main() { if orig_args.iter().any(|a| a == "--version" || a == "-V") { let version_info = rustc_tools_util::get_version_info!(); - println!("{}", version_info); + println!("{version_info}"); exit(0); } diff --git a/src/main.rs b/src/main.rs index 4a32e0e54a81..fce3cdfc462e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,12 +37,12 @@ You can use tool lints to allow or deny lints from your code, eg.: "#; fn show_help() { - println!("{}", CARGO_CLIPPY_HELP); + println!("{CARGO_CLIPPY_HELP}"); } fn show_version() { let version_info = rustc_tools_util::get_version_info!(); - println!("{}", version_info); + println!("{version_info}"); } pub fn main() { @@ -133,7 +133,7 @@ impl ClippyCmd { let clippy_args: String = self .clippy_args .iter() - .map(|arg| format!("{}__CLIPPY_HACKERY__", arg)) + .map(|arg| format!("{arg}__CLIPPY_HACKERY__")) .collect(); // Currently, `CLIPPY_TERMINAL_WIDTH` is used only to format "unknown field" error messages. diff --git a/tests/compile-test.rs b/tests/compile-test.rs index ba6186e599e9..fa769222d1af 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -111,15 +111,14 @@ static EXTERN_FLAGS: LazyLock = LazyLock::new(|| { .collect(); assert!( not_found.is_empty(), - "dependencies not found in depinfo: {:?}\n\ + "dependencies not found in depinfo: {not_found:?}\n\ help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\ help: Try adding to dev-dependencies in Cargo.toml\n\ help: Be sure to also add `extern crate ...;` to tests/compile-test.rs", - not_found, ); crates .into_iter() - .map(|(name, path)| format!(" --extern {}={}", name, path)) + .map(|(name, path)| format!(" --extern {name}={path}")) .collect() }); @@ -150,9 +149,8 @@ fn base_config(test_dir: &str) -> compiletest::Config { .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display())) .unwrap_or_default(); config.target_rustcflags = Some(format!( - "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}", + "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{host_libs}{}", deps_path.display(), - host_libs, &*EXTERN_FLAGS, )); @@ -239,7 +237,7 @@ fn run_ui_toml() { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), Err(e) => { - panic!("I/O failure during tests: {:?}", e); + panic!("I/O failure during tests: {e:?}"); }, } } @@ -348,7 +346,7 @@ fn run_ui_cargo() { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), Err(e) => { - panic!("I/O failure during tests: {:?}", e); + panic!("I/O failure during tests: {e:?}"); }, } } @@ -419,16 +417,15 @@ fn check_rustfix_coverage() { if rs_path.starts_with("tests/ui/crashes") { continue; } - assert!(rs_path.starts_with("tests/ui/"), "{:?}", rs_file); + assert!(rs_path.starts_with("tests/ui/"), "{rs_file:?}"); let filename = rs_path.strip_prefix("tests/ui/").unwrap(); assert!( RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS .binary_search_by_key(&filename, Path::new) .is_ok(), - "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ + "`{rs_file}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ Please either add `// run-rustfix` at the top of the file or add the file to \ `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.", - rs_file, ); } } @@ -478,15 +475,13 @@ fn ui_cargo_toml_metadata() { .map(|component| component.as_os_str().to_string_lossy().replace('-', "_")) .any(|s| *s == name) || path.starts_with(&cargo_common_metadata_path), - "{:?} has incorrect package name", - path + "{path:?} has incorrect package name" ); let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true); assert!( !publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()), - "{:?} lacks `publish = false`", - path + "{path:?} lacks `publish = false`" ); } } diff --git a/tests/integration.rs b/tests/integration.rs index 23a9bef3ccce..818ff70b33f4 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -6,10 +6,15 @@ use std::env; use std::ffi::OsStr; use std::process::Command; +#[cfg(not(windows))] +const CARGO_CLIPPY: &str = "cargo-clippy"; +#[cfg(windows)] +const CARGO_CLIPPY: &str = "cargo-clippy.exe"; + #[cfg_attr(feature = "integration", test)] fn integration_test() { let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set"); - let repo_url = format!("https://github.com/{}", repo_name); + let repo_url = format!("https://github.com/{repo_name}"); let crate_name = repo_name .split('/') .nth(1) @@ -31,7 +36,7 @@ fn integration_test() { let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let target_dir = std::path::Path::new(&root_dir).join("target"); - let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy"); + let clippy_binary = target_dir.join(env!("PROFILE")).join(CARGO_CLIPPY); let output = Command::new(clippy_binary) .current_dir(repo_dir) @@ -51,17 +56,15 @@ fn integration_test() { .expect("unable to run clippy"); let stderr = String::from_utf8_lossy(&output.stderr); - if stderr.contains("internal compiler error") { - let backtrace_start = stderr - .find("thread 'rustc' panicked at") - .expect("start of backtrace not found"); - let backtrace_end = stderr - .rfind("error: internal compiler error") + if let Some(backtrace_start) = stderr.find("error: internal compiler error") { + static BACKTRACE_END_MSG: &str = "end of query stack"; + let backtrace_end = stderr[backtrace_start..] + .find(BACKTRACE_END_MSG) .expect("end of backtrace not found"); panic!( "internal compiler error\nBacktrace:\n\n{}", - &stderr[backtrace_start..backtrace_end] + &stderr[backtrace_start..backtrace_start + backtrace_end + BACKTRACE_END_MSG.len()] ); } else if stderr.contains("query stack during panic") { panic!("query stack during panic in the output"); @@ -83,7 +86,7 @@ fn integration_test() { match output.status.code() { Some(0) => println!("Compilation successful"), - Some(code) => eprintln!("Compilation failed. Exit code: {}", code), + Some(code) => eprintln!("Compilation failed. Exit code: {code}"), None => panic!("Process terminated by signal"), } } diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 2e0f4e76075b..abd0d1bc5934 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -102,7 +102,7 @@ fn lint_message_convention() { "error: the test '{}' contained the following nonconforming lines :", message.path.display() ); - message.bad_lines.iter().for_each(|line| eprintln!("{}", line)); + message.bad_lines.iter().for_each(|line| eprintln!("{line}")); eprintln!("\n\n"); } diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 7d6edc2b1e09..caedd5d76cd6 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -17,7 +17,7 @@ fn test_missing_tests() { "Didn't see a test file for the following files:\n\n{}\n", missing_files .iter() - .map(|s| format!("\t{}", s)) + .map(|s| format!("\t{s}")) .collect::>() .join("\n") ); diff --git a/tests/ui-cargo/duplicate_mod/fail/src/main.stderr b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr index b450a2b18f25..3b80d89a6865 100644 --- a/tests/ui-cargo/duplicate_mod/fail/src/main.stderr +++ b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr @@ -7,8 +7,8 @@ LL | / #[path = "b.rs"] LL | | mod b2; | |_______^ loaded again here | - = note: `-D clippy::duplicate-mod` implied by `-D warnings` = help: replace all but one `mod` item with `use` items + = note: `-D clippy::duplicate-mod` implied by `-D warnings` error: file is loaded as a module multiple times: `$DIR/c.rs` --> $DIR/main.rs:9:1 diff --git a/tests/ui-cargo/feature_name/fail/src/main.stderr b/tests/ui-cargo/feature_name/fail/src/main.stderr index b9e6cb49bc98..c6a11fa93eb5 100644 --- a/tests/ui-cargo/feature_name/fail/src/main.stderr +++ b/tests/ui-cargo/feature_name/fail/src/main.stderr @@ -1,7 +1,7 @@ error: the "no-" prefix in the feature name "no-qaq" is negative | - = note: `-D clippy::negative-feature-names` implied by `-D warnings` = help: consider renaming the feature to "qaq", but make sure the feature adds functionality + = note: `-D clippy::negative-feature-names` implied by `-D warnings` error: the "no_" prefix in the feature name "no_qaq" is negative | @@ -17,8 +17,8 @@ error: the "not_" prefix in the feature name "not_orz" is negative error: the "-support" suffix in the feature name "qvq-support" is redundant | - = note: `-D clippy::redundant-feature-names` implied by `-D warnings` = help: consider renaming the feature to "qvq" + = note: `-D clippy::redundant-feature-names` implied by `-D warnings` error: the "_support" suffix in the feature name "qvq_support" is redundant | diff --git a/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_mod/src/main.stderr index e2010e998131..697c8b57c4a2 100644 --- a/tests/ui-cargo/module_style/fail_mod/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_mod/src/main.stderr @@ -4,8 +4,8 @@ error: `mod.rs` files are required, found `bad/inner.rs` LL | pub mod stuff; | ^ | - = note: `-D clippy::self-named-module-files` implied by `-D warnings` = help: move `bad/inner.rs` to `bad/inner/mod.rs` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` error: `mod.rs` files are required, found `bad/inner/stuff.rs` --> $DIR/bad/inner/stuff.rs:1:1 diff --git a/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr b/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr index 46991ff662e5..ea6ea98064a7 100644 --- a/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_mod_remap/src/main.stderr @@ -4,8 +4,8 @@ error: `mod.rs` files are required, found `bad.rs` LL | pub mod inner; | ^ | - = note: `-D clippy::self-named-module-files` implied by `-D warnings` = help: move `bad.rs` to `bad/mod.rs` + = note: `-D clippy::self-named-module-files` implied by `-D warnings` error: aborting due to previous error diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr index f91940209383..f40ceea234b9 100644 --- a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr +++ b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr @@ -4,8 +4,8 @@ error: `mod.rs` files are not allowed, found `bad/mod.rs` LL | pub struct Thing; | ^ | - = note: `-D clippy::mod-module-files` implied by `-D warnings` = help: move `bad/mod.rs` to `bad.rs` + = note: `-D clippy::mod-module-files` implied by `-D warnings` error: aborting due to previous error diff --git a/tests/ui-internal/auxiliary/paths.rs b/tests/ui-internal/auxiliary/paths.rs new file mode 100644 index 000000000000..52fcaec4df32 --- /dev/null +++ b/tests/ui-internal/auxiliary/paths.rs @@ -0,0 +1,2 @@ +pub static OPTION: [&str; 3] = ["core", "option", "Option"]; +pub const RESULT: &[&str] = &["core", "result", "Result"]; diff --git a/tests/ui-internal/check_clippy_version_attribute.stderr b/tests/ui-internal/check_clippy_version_attribute.stderr index 2aa4de490bcf..fd8c8379f5bf 100644 --- a/tests/ui-internal/check_clippy_version_attribute.stderr +++ b/tests/ui-internal/check_clippy_version_attribute.stderr @@ -10,13 +10,13 @@ LL | | report_in_external_macro: true LL | | } | |_^ | + = help: please use a valid semantic version, see `doc/adding_lints.md` note: the lint level is defined here --> $DIR/check_clippy_version_attribute.rs:1:9 | LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` - = help: please use a valid semantic version, see `doc/adding_lints.md` = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this item has an invalid `clippy::version` attribute @@ -46,8 +46,8 @@ LL | | report_in_external_macro: true LL | | } | |_^ | - = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = help: please use a `clippy::version` attribute, see `doc/adding_lints.md` + = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]` = note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info) error: this lint is missing the `clippy::version` attribute or version value diff --git a/tests/ui-internal/if_chain_style.stderr b/tests/ui-internal/if_chain_style.stderr index 24106510e73b..d8f1ffb21ba6 100644 --- a/tests/ui-internal/if_chain_style.stderr +++ b/tests/ui-internal/if_chain_style.stderr @@ -10,12 +10,12 @@ LL | | } LL | | } | |_____^ | - = note: `-D clippy::if-chain-style` implied by `-D warnings` help: this `let` statement can also be in the `if_chain!` --> $DIR/if_chain_style.rs:10:9 | LL | let x = ""; | ^^^^^^^^^^^ + = note: `-D clippy::if-chain-style` implied by `-D warnings` error: `if a && b;` should be `if a; if b;` --> $DIR/if_chain_style.rs:19:12 diff --git a/tests/ui-internal/match_type_on_diag_item.rs b/tests/ui-internal/match_type_on_diag_item.rs deleted file mode 100644 index 4b41ff15e80f..000000000000 --- a/tests/ui-internal/match_type_on_diag_item.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] -#![feature(rustc_private)] - -extern crate clippy_utils; -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; - -#[macro_use] -extern crate rustc_session; -use clippy_utils::{paths, ty::match_type}; -use rustc_hir::Expr; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; - -declare_lint! { - pub TEST_LINT, - Warn, - "" -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -static OPTION: [&str; 3] = ["core", "option", "Option"]; - -impl<'tcx> LateLintPass<'tcx> for Pass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) { - let ty = cx.typeck_results().expr_ty(expr); - - let _ = match_type(cx, ty, &OPTION); - let _ = match_type(cx, ty, &["core", "result", "Result"]); - - let rc_path = &["alloc", "rc", "Rc"]; - let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - } -} - -fn main() {} diff --git a/tests/ui-internal/match_type_on_diag_item.stderr b/tests/ui-internal/match_type_on_diag_item.stderr deleted file mode 100644 index e3cb6b6c22ea..000000000000 --- a/tests/ui-internal/match_type_on_diag_item.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:31:17 - | -LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Option)` - | -note: the lint level is defined here - --> $DIR/match_type_on_diag_item.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` - -error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:32:17 - | -LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Result)` - -error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:35:17 - | -LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)` - -error: aborting due to 3 previous errors - diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed new file mode 100644 index 000000000000..4c050332f2cc --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -0,0 +1,62 @@ +// run-rustfix +// aux-build:paths.rs +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate clippy_utils; +extern crate paths; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_span; + +#[allow(unused)] +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; +#[allow(unused)] +use clippy_utils::{ + is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method, + match_def_path, match_trait_method, path_res, +}; + +#[allow(unused)] +use rustc_hir::LangItem; +#[allow(unused)] +use rustc_span::sym; + +use rustc_hir::def_id::DefId; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +#[allow(unused)] +static OPTION: [&str; 3] = ["core", "option", "Option"]; +#[allow(unused)] +const RESULT: &[&str] = &["core", "result", "Result"]; + +fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { + let _ = is_type_diagnostic_item(cx, ty, sym::Option); + let _ = is_type_diagnostic_item(cx, ty, sym::Result); + let _ = is_type_diagnostic_item(cx, ty, sym::Result); + + #[allow(unused)] + let rc_path = &["alloc", "rc", "Rc"]; + let _ = is_type_diagnostic_item(cx, ty, sym::Rc); + + let _ = is_type_diagnostic_item(cx, ty, sym::Option); + let _ = is_type_diagnostic_item(cx, ty, sym::Result); + + let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); + let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); + + let _ = cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did); + let _ = cx.tcx.is_diagnostic_item(sym::Option, did); + let _ = cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did); + + let _ = is_trait_method(cx, expr, sym::AsRef); + + let _ = is_path_diagnostic_item(cx, expr, sym::Option); + let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id)); + let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); +} + +fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs new file mode 100644 index 000000000000..6506f1f164ac --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -0,0 +1,62 @@ +// run-rustfix +// aux-build:paths.rs +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate clippy_utils; +extern crate paths; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_span; + +#[allow(unused)] +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; +#[allow(unused)] +use clippy_utils::{ + is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method, + match_def_path, match_trait_method, path_res, +}; + +#[allow(unused)] +use rustc_hir::LangItem; +#[allow(unused)] +use rustc_span::sym; + +use rustc_hir::def_id::DefId; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +#[allow(unused)] +static OPTION: [&str; 3] = ["core", "option", "Option"]; +#[allow(unused)] +const RESULT: &[&str] = &["core", "result", "Result"]; + +fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { + let _ = match_type(cx, ty, &OPTION); + let _ = match_type(cx, ty, RESULT); + let _ = match_type(cx, ty, &["core", "result", "Result"]); + + #[allow(unused)] + let rc_path = &["alloc", "rc", "Rc"]; + let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + + let _ = match_type(cx, ty, &paths::OPTION); + let _ = match_type(cx, ty, paths::RESULT); + + let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); + let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); + + let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); + let _ = match_def_path(cx, did, &["core", "option", "Option"]); + let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); + + let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); + + let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); + let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); + let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); +} + +fn main() {} diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr new file mode 100644 index 000000000000..a99a8f71fa6a --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -0,0 +1,101 @@ +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:37:13 + | +LL | let _ = match_type(cx, ty, &OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` + | +note: the lint level is defined here + --> $DIR/unnecessary_def_path.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:38:13 + | +LL | let _ = match_type(cx, ty, RESULT); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:39:13 + | +LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:43:13 + | +LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:45:13 + | +LL | let _ = match_type(cx, ty, &paths::OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:46:13 + | +LL | let _ = match_type(cx, ty, paths::RESULT); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:48:13 + | +LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:49:13 + | +LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:51:13 + | +LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:52:13 + | +LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:53:13 + | +LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did)` + | + = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:55:13 + | +LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)` + +error: use of a def path to a diagnostic item + --> $DIR/unnecessary_def_path.rs:57:13 + | +LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:58:13 + | +LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id))` + +error: use of a def path to a `LangItem` + --> $DIR/unnecessary_def_path.rs:59:13 + | +LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)` + +error: aborting due to 15 previous errors + diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs index b4e677ea124b..7f1c512d7c97 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + fn main() {} #[warn(clippy::cognitive_complexity)] diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index b2b57bdde89c..630bad07c5b7 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -3,7 +3,7 @@ warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecate warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `blacklisted-names`. Please use `disallowed-names` instead error: the function has a cognitive complexity of (3/2) - --> $DIR/conf_deprecated_key.rs:4:4 + --> $DIR/conf_deprecated_key.rs:6:4 | LL | fn cognitive_complexity() { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/disallowed_macros/auxiliary/macros.rs b/tests/ui-toml/disallowed_macros/auxiliary/macros.rs new file mode 100644 index 000000000000..fcaeace0e989 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/auxiliary/macros.rs @@ -0,0 +1,32 @@ +#[macro_export] +macro_rules! expr { + () => { + 1 + }; +} + +#[macro_export] +macro_rules! stmt { + () => { + let _x = (); + }; +} + +#[macro_export] +macro_rules! ty { + () => { &'static str }; +} + +#[macro_export] +macro_rules! pat { + () => { + _ + }; +} + +#[macro_export] +macro_rules! item { + () => { + const ITEM: usize = 1; + }; +} diff --git a/tests/ui-toml/disallowed_macros/clippy.toml b/tests/ui-toml/disallowed_macros/clippy.toml new file mode 100644 index 000000000000..c8fe8be9a770 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/clippy.toml @@ -0,0 +1,11 @@ +disallowed-macros = [ + "std::println", + "std::vec", + { path = "std::cfg" }, + { path = "serde::Serialize", reason = "no serializing" }, + "macros::expr", + "macros::stmt", + "macros::ty", + "macros::pat", + "macros::item", +] diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.rs b/tests/ui-toml/disallowed_macros/disallowed_macros.rs new file mode 100644 index 000000000000..2bb5376076e2 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.rs @@ -0,0 +1,39 @@ +// aux-build:macros.rs + +#![allow(unused)] + +extern crate macros; + +use serde::Serialize; + +fn main() { + println!("one"); + println!("two"); + cfg!(unix); + vec![1, 2, 3]; + + #[derive(Serialize)] + struct Derive; + + let _ = macros::expr!(); + macros::stmt!(); + let macros::pat!() = 1; + let _: macros::ty!() = ""; + macros::item!(); + + eprintln!("allowed"); +} + +struct S; + +impl S { + macros::item!(); +} + +trait Y { + macros::item!(); +} + +impl Y for S { + macros::item!(); +} diff --git a/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr new file mode 100644 index 000000000000..aed9feb6f796 --- /dev/null +++ b/tests/ui-toml/disallowed_macros/disallowed_macros.stderr @@ -0,0 +1,84 @@ +error: use of a disallowed macro `std::println` + --> $DIR/disallowed_macros.rs:10:5 + | +LL | println!("one"); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-macros` implied by `-D warnings` + +error: use of a disallowed macro `std::println` + --> $DIR/disallowed_macros.rs:11:5 + | +LL | println!("two"); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `std::cfg` + --> $DIR/disallowed_macros.rs:12:5 + | +LL | cfg!(unix); + | ^^^^^^^^^^ + +error: use of a disallowed macro `std::vec` + --> $DIR/disallowed_macros.rs:13:5 + | +LL | vec![1, 2, 3]; + | ^^^^^^^^^^^^^ + +error: use of a disallowed macro `serde::Serialize` + --> $DIR/disallowed_macros.rs:15:14 + | +LL | #[derive(Serialize)] + | ^^^^^^^^^ + | + = note: no serializing (from clippy.toml) + +error: use of a disallowed macro `macros::expr` + --> $DIR/disallowed_macros.rs:18:13 + | +LL | let _ = macros::expr!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::stmt` + --> $DIR/disallowed_macros.rs:19:5 + | +LL | macros::stmt!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::pat` + --> $DIR/disallowed_macros.rs:20:9 + | +LL | let macros::pat!() = 1; + | ^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::ty` + --> $DIR/disallowed_macros.rs:21:12 + | +LL | let _: macros::ty!() = ""; + | ^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:22:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:30:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:34:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::item` + --> $DIR/disallowed_macros.rs:38:5 + | +LL | macros::item!(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors + diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed new file mode 100644 index 000000000000..01d135764dff --- /dev/null +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.fixed @@ -0,0 +1,62 @@ +// aux-build:proc_macro_derive.rs +// run-rustfix + +#![warn(clippy::nonstandard_macro_braces)] + +extern crate proc_macro_derive; +extern crate quote; + +use quote::quote; + +#[derive(proc_macro_derive::DeriveSomething)] +pub struct S; + +proc_macro_derive::foo_bar!(); + +#[rustfmt::skip] +macro_rules! test { + () => { + vec![0, 0, 0] + }; +} + +#[rustfmt::skip] +macro_rules! test2 { + ($($arg:tt)*) => { + format_args!($($arg)*) + }; +} + +macro_rules! type_pos { + ($what:ty) => { + Vec<$what> + }; +} + +macro_rules! printlnfoo { + ($thing:expr) => { + println!("{}", $thing) + }; +} + +#[rustfmt::skip] +fn main() { + let _ = vec![1, 2, 3]; + let _ = format!("ugh {} stop being such a good compiler", "hello"); + let _ = matches!({}, ()); + let _ = quote!{let x = 1;}; + let _ = quote::quote!{match match match}; + let _ = test!(); // trigger when macro def is inside our own crate + let _ = vec![1,2,3]; + + let _ = quote::quote! {true || false}; + let _ = vec! [0 ,0 ,0]; + let _ = format!("fds{}fds", 10); + let _ = test2!["{}{}{}", 1, 2, 3]; + + let _: type_pos![usize] = vec![]; + + eprint!["test if user config overrides defaults"]; + + printlnfoo!["test if printlnfoo is triggered by println"]; +} diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs index ed8161acc0ef..72883e8270c3 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -1,4 +1,5 @@ // aux-build:proc_macro_derive.rs +// run-rustfix #![warn(clippy::nonstandard_macro_braces)] diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr index 15fa4f42f9ba..7ae3815978c7 100644 --- a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -1,106 +1,57 @@ error: use of irregular braces for `vec!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:43:13 + --> $DIR/conf_nonstandard_macro_braces.rs:44:13 | LL | let _ = vec! {1, 2, 3}; - | ^^^^^^^^^^^^^^ - | -help: consider writing `vec![1, 2, 3]` - --> $DIR/conf_nonstandard_macro_braces.rs:43:13 + | ^^^^^^^^^^^^^^ help: consider writing: `vec![1, 2, 3]` | -LL | let _ = vec! {1, 2, 3}; - | ^^^^^^^^^^^^^^ = note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings` error: use of irregular braces for `format!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:44:13 - | -LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider writing `format!("ugh () stop being such a good compiler", "hello")` - --> $DIR/conf_nonstandard_macro_braces.rs:44:13 + --> $DIR/conf_nonstandard_macro_braces.rs:45:13 | LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `format!("ugh {} stop being such a good compiler", "hello")` error: use of irregular braces for `matches!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:45:13 - | -LL | let _ = matches!{{}, ()}; - | ^^^^^^^^^^^^^^^^ - | -help: consider writing `matches!((), ())` - --> $DIR/conf_nonstandard_macro_braces.rs:45:13 + --> $DIR/conf_nonstandard_macro_braces.rs:46:13 | LL | let _ = matches!{{}, ()}; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: consider writing: `matches!({}, ())` error: use of irregular braces for `quote!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:46:13 - | -LL | let _ = quote!(let x = 1;); - | ^^^^^^^^^^^^^^^^^^ - | -help: consider writing `quote! {let x = 1;}` - --> $DIR/conf_nonstandard_macro_braces.rs:46:13 + --> $DIR/conf_nonstandard_macro_braces.rs:47:13 | LL | let _ = quote!(let x = 1;); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: consider writing: `quote!{let x = 1;}` error: use of irregular braces for `quote::quote!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:47:13 + --> $DIR/conf_nonstandard_macro_braces.rs:48:13 | LL | let _ = quote::quote!(match match match); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider writing `quote::quote! {match match match}` - --> $DIR/conf_nonstandard_macro_braces.rs:47:13 - | -LL | let _ = quote::quote!(match match match); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `quote::quote!{match match match}` error: use of irregular braces for `vec!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:18:9 + --> $DIR/conf_nonstandard_macro_braces.rs:19:9 | LL | vec!{0, 0, 0} - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: consider writing: `vec![0, 0, 0]` ... LL | let _ = test!(); // trigger when macro def is inside our own crate | ------- in this macro invocation | -help: consider writing `vec![0, 0, 0]` - --> $DIR/conf_nonstandard_macro_braces.rs:18:9 - | -LL | vec!{0, 0, 0} - | ^^^^^^^^^^^^^ -... -LL | let _ = test!(); // trigger when macro def is inside our own crate - | ------- in this macro invocation = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) error: use of irregular braces for `type_pos!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:56:12 + --> $DIR/conf_nonstandard_macro_braces.rs:57:12 | LL | let _: type_pos!(usize) = vec![]; - | ^^^^^^^^^^^^^^^^ - | -help: consider writing `type_pos![usize]` - --> $DIR/conf_nonstandard_macro_braces.rs:56:12 - | -LL | let _: type_pos!(usize) = vec![]; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: consider writing: `type_pos![usize]` error: use of irregular braces for `eprint!` macro - --> $DIR/conf_nonstandard_macro_braces.rs:58:5 - | -LL | eprint!("test if user config overrides defaults"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider writing `eprint!["test if user config overrides defaults"]` - --> $DIR/conf_nonstandard_macro_braces.rs:58:5 + --> $DIR/conf_nonstandard_macro_braces.rs:59:5 | LL | eprint!("test if user config overrides defaults"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `eprint!["test if user config overrides defaults"]` error: aborting due to 8 previous errors diff --git a/tests/ui-toml/toml_disallowed_methods/clippy.toml b/tests/ui-toml/toml_disallowed_methods/clippy.toml index c902d21123dc..28774db625bb 100644 --- a/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -3,6 +3,7 @@ disallowed-methods = [ "std::iter::Iterator::sum", "f32::clamp", "slice::sort_unstable", + "futures::stream::select_all", # can give path and reason with an inline table { path = "regex::Regex::is_match", reason = "no matching allowed" }, # can use an inline table but omit reason diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index 3397fa1ec692..b483f1600289 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -1,6 +1,9 @@ #![warn(clippy::disallowed_methods)] +extern crate futures; extern crate regex; + +use futures::stream::{empty, select_all}; use regex::Regex; fn main() { @@ -20,4 +23,7 @@ fn main() { let in_call = Box::new(f32::clamp); let in_method_call = ["^", "$"].into_iter().map(Regex::new); + + // resolve ambiguity between `futures::stream::select_all` the module and the function + let same_name_as_module = select_all(vec![empty::<()>()]); } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 5cbb567546c0..6d78c32e127e 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:7:14 + --> $DIR/conf_disallowed_methods.rs:10:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); = note: `-D clippy::disallowed-methods` implied by `-D warnings` error: use of a disallowed method `regex::Regex::is_match` - --> $DIR/conf_disallowed_methods.rs:8:5 + --> $DIR/conf_disallowed_methods.rs:11:5 | LL | re.is_match("abc"); | ^^^^^^^^^^^^^^^^^^ @@ -15,40 +15,46 @@ LL | re.is_match("abc"); = note: no matching allowed (from clippy.toml) error: use of a disallowed method `std::iter::Iterator::sum` - --> $DIR/conf_disallowed_methods.rs:11:5 + --> $DIR/conf_disallowed_methods.rs:14:5 | LL | a.iter().sum::(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `slice::sort_unstable` - --> $DIR/conf_disallowed_methods.rs:13:5 + --> $DIR/conf_disallowed_methods.rs:16:5 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:15:13 + --> $DIR/conf_disallowed_methods.rs:18:13 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:18:61 + --> $DIR/conf_disallowed_methods.rs:21:61 | LL | let indirect: fn(&str) -> Result = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:21:28 + --> $DIR/conf_disallowed_methods.rs:24:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:22:53 + --> $DIR/conf_disallowed_methods.rs:25:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ -error: aborting due to 8 previous errors +error: use of a disallowed method `futures::stream::select_all` + --> $DIR/conf_disallowed_methods.rs:28:31 + | +LL | let same_name_as_module = select_all(vec![empty::<()>()]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index f27f78d15d3a..82ee80541321 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -11,6 +11,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie cargo-ignore-publish cognitive-complexity-threshold cyclomatic-complexity-threshold + disallowed-macros disallowed-methods disallowed-names disallowed-types diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index dd24f5aa592b..b25e68f13061 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -2,15 +2,41 @@ clippy::assign_op_pattern, clippy::erasing_op, clippy::identity_op, + clippy::op_ref, clippy::unnecessary_owned_empty_strings, arithmetic_overflow, unconditional_panic )] -#![feature(inline_const, saturating_int_impl)] +#![feature(const_mut_refs, inline_const, saturating_int_impl)] #![warn(clippy::arithmetic_side_effects)] use core::num::{Saturating, Wrapping}; +pub struct Custom; + +macro_rules! impl_arith { + ( $( $_trait:ident, $ty:ty, $method:ident; )* ) => { + $( + impl core::ops::$_trait<$ty> for Custom { + type Output = Self; + fn $method(self, _: $ty) -> Self::Output { Self } + } + )* + } +} + +impl_arith!( + Add, i32, add; + Div, i32, div; + Mul, i32, mul; + Sub, i32, sub; + + Add, f64, add; + Div, f64, div; + Mul, f64, mul; + Sub, f64, sub; +); + pub fn association_with_structures_should_not_trigger_the_lint() { enum Foo { Bar = -2, @@ -79,32 +105,48 @@ pub fn const_ops_should_not_trigger_the_lint() { const _: i32 = 1 + 1; let _ = const { 1 + 1 }; - const _: i32 = { let mut n = -1; n = -(-1); n = -n; n }; - let _ = const { let mut n = -1; n = -(-1); n = -n; n }; + const _: i32 = { let mut n = 1; n = -1; n = -(-1); n = -n; n }; + let _ = const { let mut n = 1; n = -1; n = -(-1); n = -n; n }; } -pub fn non_overflowing_runtime_ops_or_ops_already_handled_by_the_compiler() { +pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_trigger_the_lint() { let mut _n = i32::MAX; // Assign _n += 0; + _n += &0; _n -= 0; + _n -= &0; _n /= 99; + _n /= &99; _n %= 99; + _n %= &99; _n *= 0; + _n *= &0; _n *= 1; + _n *= &1; // Binary _n = _n + 0; + _n = _n + &0; _n = 0 + _n; + _n = &0 + _n; _n = _n - 0; + _n = _n - &0; _n = 0 - _n; + _n = &0 - _n; _n = _n / 99; + _n = _n / &99; _n = _n % 99; + _n = _n % &99; _n = _n * 0; + _n = _n * &0; _n = 0 * _n; + _n = &0 * _n; _n = _n * 1; + _n = _n * &1; _n = 1 * _n; + _n = &1 * _n; _n = 23 + 85; // Unary @@ -112,28 +154,71 @@ pub fn non_overflowing_runtime_ops_or_ops_already_handled_by_the_compiler() { _n = -(-1); } -pub fn overflowing_runtime_ops() { +pub fn runtime_ops() { let mut _n = i32::MAX; // Assign _n += 1; + _n += &1; _n -= 1; + _n -= &1; _n /= 0; + _n /= &0; _n %= 0; + _n %= &0; _n *= 2; + _n *= &2; // Binary _n = _n + 1; + _n = _n + &1; _n = 1 + _n; + _n = &1 + _n; _n = _n - 1; + _n = _n - &1; _n = 1 - _n; + _n = &1 - _n; _n = _n / 0; + _n = _n / &0; _n = _n % 0; + _n = _n % &0; _n = _n * 2; + _n = _n * &2; _n = 2 * _n; + _n = &2 * _n; + _n = 23 + &85; + _n = &23 + 85; + _n = &23 + &85; + + // Custom + let _ = Custom + 0; + let _ = Custom + 1; + let _ = Custom + 2; + let _ = Custom + 0.0; + let _ = Custom + 1.0; + let _ = Custom + 2.0; + let _ = Custom - 0; + let _ = Custom - 1; + let _ = Custom - 2; + let _ = Custom - 0.0; + let _ = Custom - 1.0; + let _ = Custom - 2.0; + let _ = Custom / 0; + let _ = Custom / 1; + let _ = Custom / 2; + let _ = Custom / 0.0; + let _ = Custom / 1.0; + let _ = Custom / 2.0; + let _ = Custom * 0; + let _ = Custom * 1; + let _ = Custom * 2; + let _ = Custom * 0.0; + let _ = Custom * 1.0; + let _ = Custom * 2.0; // Unary _n = -_n; + _n = -&_n; } fn main() {} diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index a2a856efbffc..0f06e22bae96 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,88 +1,352 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:119:5 + --> $DIR/arithmetic_side_effects.rs:78:13 + | +LL | let _ = String::new() + ""; + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:86:27 + | +LL | let inferred_string = string + ""; + | ^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:90:13 + | +LL | let _ = inferred_string + ""; + | ^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:161:5 | LL | _n += 1; | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:162:5 | - = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` +LL | _n += &1; + | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:120:5 + --> $DIR/arithmetic_side_effects.rs:163:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:121:5 + --> $DIR/arithmetic_side_effects.rs:164:5 + | +LL | _n -= &1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:165:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:122:5 + --> $DIR/arithmetic_side_effects.rs:166:5 + | +LL | _n /= &0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:167:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:123:5 + --> $DIR/arithmetic_side_effects.rs:168:5 + | +LL | _n %= &0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:169:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:126:10 + --> $DIR/arithmetic_side_effects.rs:170:5 + | +LL | _n *= &2; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:173:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:127:10 + --> $DIR/arithmetic_side_effects.rs:174:10 + | +LL | _n = _n + &1; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:175:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:128:10 + --> $DIR/arithmetic_side_effects.rs:176:10 + | +LL | _n = &1 + _n; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:177:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:129:10 + --> $DIR/arithmetic_side_effects.rs:178:10 + | +LL | _n = _n - &1; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:179:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:130:10 + --> $DIR/arithmetic_side_effects.rs:180:10 + | +LL | _n = &1 - _n; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:181:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:131:10 + --> $DIR/arithmetic_side_effects.rs:182:10 + | +LL | _n = _n / &0; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:183:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:132:10 + --> $DIR/arithmetic_side_effects.rs:184:10 + | +LL | _n = _n % &0; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:185:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:133:10 + --> $DIR/arithmetic_side_effects.rs:186:10 + | +LL | _n = _n * &2; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:187:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:136:10 + --> $DIR/arithmetic_side_effects.rs:188:10 + | +LL | _n = &2 * _n; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:189:10 + | +LL | _n = 23 + &85; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:190:10 + | +LL | _n = &23 + 85; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:191:10 + | +LL | _n = &23 + &85; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:194:13 + | +LL | let _ = Custom + 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:195:13 + | +LL | let _ = Custom + 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:196:13 + | +LL | let _ = Custom + 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:197:13 + | +LL | let _ = Custom + 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:198:13 + | +LL | let _ = Custom + 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:199:13 + | +LL | let _ = Custom + 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:200:13 + | +LL | let _ = Custom - 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:201:13 + | +LL | let _ = Custom - 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:202:13 + | +LL | let _ = Custom - 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:203:13 + | +LL | let _ = Custom - 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:204:13 + | +LL | let _ = Custom - 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:205:13 + | +LL | let _ = Custom - 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:206:13 + | +LL | let _ = Custom / 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:207:13 + | +LL | let _ = Custom / 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:208:13 + | +LL | let _ = Custom / 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:209:13 + | +LL | let _ = Custom / 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:210:13 + | +LL | let _ = Custom / 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:211:13 + | +LL | let _ = Custom / 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:212:13 + | +LL | let _ = Custom * 0; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:213:13 + | +LL | let _ = Custom * 1; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:214:13 + | +LL | let _ = Custom * 2; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:215:13 + | +LL | let _ = Custom * 0.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:216:13 + | +LL | let _ = Custom * 1.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:217:13 + | +LL | let _ = Custom * 2.0; + | ^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:220:10 | LL | _n = -_n; | ^^^ -error: aborting due to 14 previous errors +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:221:10 + | +LL | _n = -&_n; + | ^^^^ + +error: aborting due to 58 previous errors diff --git a/tests/ui/assign_ops2.rs b/tests/ui/assign_ops2.rs index f6d3a8fa3f0d..2c876a96c55d 100644 --- a/tests/ui/assign_ops2.rs +++ b/tests/ui/assign_ops2.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + #[allow(unused_assignments)] #[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { diff --git a/tests/ui/assign_ops2.stderr b/tests/ui/assign_ops2.stderr index 04b1dc93d4a4..25e74602244d 100644 --- a/tests/ui/assign_ops2.stderr +++ b/tests/ui/assign_ops2.stderr @@ -1,5 +1,5 @@ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:5:5 + --> $DIR/assign_ops2.rs:7:5 | LL | a += a + 1; | ^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | a = a + a + 1; | ~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:6:5 + --> $DIR/assign_ops2.rs:8:5 | LL | a += 1 + a; | ^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | a = a + 1 + a; | ~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:7:5 + --> $DIR/assign_ops2.rs:9:5 | LL | a -= a - 1; | ^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | a = a - (a - 1); | ~~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:8:5 + --> $DIR/assign_ops2.rs:10:5 | LL | a *= a * 99; | ^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | a = a * a * 99; | ~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:9:5 + --> $DIR/assign_ops2.rs:11:5 | LL | a *= 42 * a; | ^^^^^^^^^^^ @@ -75,7 +75,7 @@ LL | a = a * 42 * a; | ~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:10:5 + --> $DIR/assign_ops2.rs:12:5 | LL | a /= a / 2; | ^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | a = a / (a / 2); | ~~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:11:5 + --> $DIR/assign_ops2.rs:13:5 | LL | a %= a % 5; | ^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | a = a % (a % 5); | ~~~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:12:5 + --> $DIR/assign_ops2.rs:14:5 | LL | a &= a & 1; | ^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | a = a & a & 1; | ~~~~~~~~~~~~~ error: variable appears on both sides of an assignment operation - --> $DIR/assign_ops2.rs:13:5 + --> $DIR/assign_ops2.rs:15:5 | LL | a *= a * a; | ^^^^^^^^^^ @@ -135,7 +135,7 @@ LL | a = a * a * a; | ~~~~~~~~~~~~~ error: manual implementation of an assign operation - --> $DIR/assign_ops2.rs:50:5 + --> $DIR/assign_ops2.rs:52:5 | LL | buf = buf + cows.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index ae2cc2492f41..4914f14b58ff 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -4,7 +4,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)] #![allow(incomplete_features)] -#![allow(clippy::useless_conversion)] +#![allow(clippy::useless_conversion, clippy::uninlined_format_args)] extern crate proc_macro; extern crate quote; diff --git a/tests/ui/bind_instead_of_map.fixed b/tests/ui/bind_instead_of_map.fixed index 5815550d7a6a..d94e2ac6072d 100644 --- a/tests/ui/bind_instead_of_map.fixed +++ b/tests/ui/bind_instead_of_map.fixed @@ -1,5 +1,6 @@ // run-rustfix #![deny(clippy::bind_instead_of_map)] +#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.rs b/tests/ui/bind_instead_of_map.rs index 623b100a4ce7..86f31f58284a 100644 --- a/tests/ui/bind_instead_of_map.rs +++ b/tests/ui/bind_instead_of_map.rs @@ -1,5 +1,6 @@ // run-rustfix #![deny(clippy::bind_instead_of_map)] +#![allow(clippy::uninlined_format_args)] // need a main anyway, use it get rid of unused warnings too pub fn main() { diff --git a/tests/ui/bind_instead_of_map.stderr b/tests/ui/bind_instead_of_map.stderr index 24c6b7f9ef32..b6a81d21bb20 100644 --- a/tests/ui/bind_instead_of_map.stderr +++ b/tests/ui/bind_instead_of_map.stderr @@ -1,5 +1,5 @@ error: using `Option.and_then(Some)`, which is a no-op - --> $DIR/bind_instead_of_map.rs:8:13 + --> $DIR/bind_instead_of_map.rs:9:13 | LL | let _ = x.and_then(Some); | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` @@ -11,13 +11,13 @@ LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map.rs:9:13 + --> $DIR/bind_instead_of_map.rs:10:13 | LL | let _ = x.and_then(|o| Some(o + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)` error: using `Result.and_then(Ok)`, which is a no-op - --> $DIR/bind_instead_of_map.rs:15:13 + --> $DIR/bind_instead_of_map.rs:16:13 | LL | let _ = x.and_then(Ok); | ^^^^^^^^^^^^^^ help: use the expression directly: `x` diff --git a/tests/ui/borrow_box.rs b/tests/ui/borrow_box.rs index 35ed87b0f182..3b5b6bf4c950 100644 --- a/tests/ui/borrow_box.rs +++ b/tests/ui/borrow_box.rs @@ -1,7 +1,6 @@ #![deny(clippy::borrowed_box)] -#![allow(clippy::disallowed_names)] -#![allow(unused_variables)] -#![allow(dead_code)] +#![allow(dead_code, unused_variables)] +#![allow(clippy::uninlined_format_args, clippy::disallowed_names)] use std::fmt::Display; diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index 3eac32815be3..99cb60a1ead9 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:21:14 + --> $DIR/borrow_box.rs:20:14 | LL | let foo: &Box; | ^^^^^^^^^^ help: try: `&bool` @@ -11,55 +11,55 @@ LL | #![deny(clippy::borrowed_box)] | ^^^^^^^^^^^^^^^^^^^^ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:25:10 + --> $DIR/borrow_box.rs:24:10 | LL | foo: &'a Box, | ^^^^^^^^^^^^^ help: try: `&'a bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:29:17 + --> $DIR/borrow_box.rs:28:17 | LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:95:25 + --> $DIR/borrow_box.rs:94:25 | LL | pub fn test14(_display: &Box) {} | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:96:25 + --> $DIR/borrow_box.rs:95:25 | LL | pub fn test15(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:97:29 + --> $DIR/borrow_box.rs:96:29 | LL | pub fn test16<'a>(_display: &'a Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:99:25 + --> $DIR/borrow_box.rs:98:25 | LL | pub fn test17(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:100:25 + --> $DIR/borrow_box.rs:99:25 | LL | pub fn test18(_display: &Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:101:29 + --> $DIR/borrow_box.rs:100:29 | LL | pub fn test19<'a>(_display: &'a Box) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:106:25 + --> $DIR/borrow_box.rs:105:25 | LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` diff --git a/tests/ui/box_collection.rs b/tests/ui/box_collection.rs index 0780c8f0586e..4c9947b9ae72 100644 --- a/tests/ui/box_collection.rs +++ b/tests/ui/box_collection.rs @@ -15,7 +15,7 @@ macro_rules! boxit { } fn test_macro() { - boxit!(Vec::new(), Vec); + boxit!(vec![1], Vec); } fn test1(foo: Box>) {} @@ -50,7 +50,7 @@ fn test_local_not_linted() { pub fn pub_test(foo: Box>) {} pub fn pub_test_ret() -> Box> { - Box::new(Vec::new()) + Box::default() } fn main() {} diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs new file mode 100644 index 000000000000..dc522705bc62 --- /dev/null +++ b/tests/ui/box_default.rs @@ -0,0 +1,31 @@ +#![warn(clippy::box_default)] + +#[derive(Default)] +struct ImplementsDefault; + +struct OwnDefault; + +impl OwnDefault { + fn default() -> Self { + Self + } +} + +macro_rules! outer { + ($e: expr) => { + $e + }; +} + +fn main() { + let _string: Box = Box::new(Default::default()); + let _byte = Box::new(u8::default()); + let _vec = Box::new(Vec::::new()); + let _impl = Box::new(ImplementsDefault::default()); + let _impl2 = Box::new(::default()); + let _impl3: Box = Box::new(Default::default()); + let _own = Box::new(OwnDefault::default()); // should not lint + let _in_macro = outer!(Box::new(String::new())); + // false negative: default is from different expansion + let _vec2: Box> = Box::new(vec![]); +} diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr new file mode 100644 index 000000000000..b2030e95acb1 --- /dev/null +++ b/tests/ui/box_default.stderr @@ -0,0 +1,59 @@ +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:21:32 + | +LL | let _string: Box = Box::new(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + = note: `-D clippy::box-default` implied by `-D warnings` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:22:17 + | +LL | let _byte = Box::new(u8::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:23:16 + | +LL | let _vec = Box::new(Vec::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:24:17 + | +LL | let _impl = Box::new(ImplementsDefault::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:25:18 + | +LL | let _impl2 = Box::new(::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:26:42 + | +LL | let _impl3: Box = Box::new(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:28:28 + | +LL | let _in_macro = outer!(Box::new(String::new())); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Box::default()` instead + +error: aborting due to 7 previous errors + diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs index 12f550d9c9a8..6a63008b5a74 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -1,5 +1,6 @@ -#![allow(dead_code, clippy::equatable_if_let)] #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![allow(dead_code)] +#![allow(clippy::equatable_if_let, clippy::uninlined_format_args)] // This tests the branches_sharing_code lint at the end of blocks diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_bottom.stderr index b919812e098a..b9b113dc0c6a 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_bottom.stderr @@ -1,5 +1,5 @@ error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:30:5 + --> $DIR/shared_at_bottom.rs:31:5 | LL | / let result = false; LL | | println!("Block end!"); @@ -9,7 +9,7 @@ LL | | }; | = note: the end suggestion probably needs some adjustments to use the expression result correctly note: the lint level is defined here - --> $DIR/shared_at_bottom.rs:2:36 + --> $DIR/shared_at_bottom.rs:1:36 | LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL ~ result; | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:48:5 + --> $DIR/shared_at_bottom.rs:49:5 | LL | / println!("Same end of block"); LL | | } @@ -35,7 +35,7 @@ LL + println!("Same end of block"); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:65:5 + --> $DIR/shared_at_bottom.rs:66:5 | LL | / println!( LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", @@ -54,7 +54,7 @@ LL + ); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:77:9 + --> $DIR/shared_at_bottom.rs:78:9 | LL | / println!("Hello World"); LL | | } @@ -67,7 +67,7 @@ LL + println!("Hello World"); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:93:5 + --> $DIR/shared_at_bottom.rs:94:5 | LL | / let later_used_value = "A string value"; LL | | println!("{}", later_used_value); @@ -84,7 +84,7 @@ LL + println!("{}", later_used_value); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:106:5 + --> $DIR/shared_at_bottom.rs:107:5 | LL | / let simple_examples = "I now identify as a &str :)"; LL | | println!("This is the new simple_example: {}", simple_examples); @@ -100,7 +100,7 @@ LL + println!("This is the new simple_example: {}", simple_examples); | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:171:5 + --> $DIR/shared_at_bottom.rs:172:5 | LL | / x << 2 LL | | }; @@ -114,7 +114,7 @@ LL ~ x << 2; | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:178:5 + --> $DIR/shared_at_bottom.rs:179:5 | LL | / x * 4 LL | | } @@ -128,7 +128,7 @@ LL + x * 4 | error: all if blocks contain the same code at the end - --> $DIR/shared_at_bottom.rs:190:44 + --> $DIR/shared_at_bottom.rs:191:44 | LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } | ^^^^^^^^^^^ diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs index bdeb0a39558d..9e0b99f16665 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.rs +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -1,5 +1,6 @@ -#![allow(dead_code, clippy::mixed_read_write_in_expression)] -#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] +#![allow(dead_code)] +#![allow(clippy::mixed_read_write_in_expression, clippy::uninlined_format_args)] // This tests the branches_sharing_code lint at the start of blocks diff --git a/tests/ui/branches_sharing_code/shared_at_top.stderr b/tests/ui/branches_sharing_code/shared_at_top.stderr index fb3da641fb5e..3e3242a75d30 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top.stderr @@ -1,15 +1,15 @@ error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:10:5 + --> $DIR/shared_at_top.rs:11:5 | LL | / if true { LL | | println!("Hello World!"); | |_________________________________^ | note: the lint level is defined here - --> $DIR/shared_at_top.rs:2:36 + --> $DIR/shared_at_top.rs:1:9 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider moving these statements before the if | LL ~ println!("Hello World!"); @@ -17,7 +17,7 @@ LL + if true { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:19:5 + --> $DIR/shared_at_top.rs:20:5 | LL | / if x == 0 { LL | | let y = 9; @@ -35,7 +35,7 @@ LL + if x == 0 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:40:5 + --> $DIR/shared_at_top.rs:41:5 | LL | / let _ = if x == 7 { LL | | let y = 16; @@ -48,7 +48,7 @@ LL + let _ = if x == 7 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:58:5 + --> $DIR/shared_at_top.rs:59:5 | LL | / if x == 10 { LL | | let used_value_name = "Different type"; @@ -64,7 +64,7 @@ LL + if x == 10 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:72:5 + --> $DIR/shared_at_top.rs:73:5 | LL | / if x == 11 { LL | | let can_be_overridden = "Move me"; @@ -80,7 +80,7 @@ LL + if x == 11 { | error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:88:5 + --> $DIR/shared_at_top.rs:89:5 | LL | / if x == 2020 { LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); @@ -95,7 +95,7 @@ LL + if x == 2020 { | error: this `if` has identical blocks - --> $DIR/shared_at_top.rs:96:18 + --> $DIR/shared_at_top.rs:97:18 | LL | if x == 2019 { | __________________^ @@ -104,7 +104,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/shared_at_top.rs:98:12 + --> $DIR/shared_at_top.rs:99:12 | LL | } else { | ____________^ @@ -112,10 +112,10 @@ LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); LL | | } | |_____^ note: the lint level is defined here - --> $DIR/shared_at_top.rs:2:9 + --> $DIR/shared_at_top.rs:1:40 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 7 previous errors diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs index deefdad32c9d..93b8c6e10dae 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs @@ -1,5 +1,6 @@ +#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] #![allow(dead_code)] -#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![allow(clippy::uninlined_format_args)] // branches_sharing_code at the top and bottom of the if blocks diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr index 3edb8e53a7d4..ccd697a42155 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -1,5 +1,5 @@ error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:16:5 + --> $DIR/shared_at_top_and_bottom.rs:17:5 | LL | / if x == 7 { LL | | let t = 7; @@ -8,16 +8,16 @@ LL | | let _overlap_end = 2 * t; | |_________________________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:28:5 + --> $DIR/shared_at_top_and_bottom.rs:29:5 | LL | / let _u = 9; LL | | } | |_____^ note: the lint level is defined here - --> $DIR/shared_at_top_and_bottom.rs:2:36 + --> $DIR/shared_at_top_and_bottom.rs:1:9 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider moving these statements before the if | LL ~ let t = 7; @@ -32,7 +32,7 @@ LL + let _u = 9; | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:32:5 + --> $DIR/shared_at_top_and_bottom.rs:33:5 | LL | / if x == 99 { LL | | let r = 7; @@ -41,7 +41,7 @@ LL | | let _overlap_middle = r * r; | |____________________________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:43:5 + --> $DIR/shared_at_top_and_bottom.rs:44:5 | LL | / let _overlap_end = r * r * r; LL | | let z = "end"; @@ -63,7 +63,7 @@ LL + let z = "end"; | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:61:5 + --> $DIR/shared_at_top_and_bottom.rs:62:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { LL | | let a = 0xcafe; @@ -72,7 +72,7 @@ LL | | let e_id = gen_id(a, b); | |________________________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:81:5 + --> $DIR/shared_at_top_and_bottom.rs:82:5 | LL | / let pack = DataPack { LL | | id: e_id, @@ -102,14 +102,14 @@ LL + process_data(pack); | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:94:5 + --> $DIR/shared_at_top_and_bottom.rs:95:5 | LL | / let _ = if x == 7 { LL | | let _ = 19; | |___________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:103:5 + --> $DIR/shared_at_top_and_bottom.rs:104:5 | LL | / x << 2 LL | | }; @@ -127,14 +127,14 @@ LL ~ x << 2; | error: all if blocks contain the same code at both the start and the end - --> $DIR/shared_at_top_and_bottom.rs:106:5 + --> $DIR/shared_at_top_and_bottom.rs:107:5 | LL | / if x == 9 { LL | | let _ = 17; | |___________________^ | note: this code is shared at the end - --> $DIR/shared_at_top_and_bottom.rs:115:5 + --> $DIR/shared_at_top_and_bottom.rs:116:5 | LL | / x * 4 LL | | } diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.rs b/tests/ui/branches_sharing_code/valid_if_blocks.rs index a26141be2373..2d6055eb6c42 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.rs +++ b/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -1,5 +1,6 @@ -#![allow(dead_code, clippy::mixed_read_write_in_expression)] -#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] +#![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] +#![allow(dead_code)] +#![allow(clippy::mixed_read_write_in_expression, clippy::uninlined_format_args)] // This tests valid if blocks that shouldn't trigger the lint diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/tests/ui/branches_sharing_code/valid_if_blocks.stderr index d2acd6d9735c..ce7fff0122f1 100644 --- a/tests/ui/branches_sharing_code/valid_if_blocks.stderr +++ b/tests/ui/branches_sharing_code/valid_if_blocks.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:104:14 + --> $DIR/valid_if_blocks.rs:105:14 | LL | if false { | ______________^ @@ -7,20 +7,20 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:105:12 + --> $DIR/valid_if_blocks.rs:106:12 | LL | } else { | ____________^ LL | | } | |_____^ note: the lint level is defined here - --> $DIR/valid_if_blocks.rs:2:9 + --> $DIR/valid_if_blocks.rs:1:40 | -LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:115:15 + --> $DIR/valid_if_blocks.rs:116:15 | LL | if x == 0 { | _______________^ @@ -31,7 +31,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:119:12 + --> $DIR/valid_if_blocks.rs:120:12 | LL | } else { | ____________^ @@ -42,19 +42,19 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:126:23 + --> $DIR/valid_if_blocks.rs:127:23 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ | note: same as this - --> $DIR/valid_if_blocks.rs:126:34 + --> $DIR/valid_if_blocks.rs:127:34 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:132:23 + --> $DIR/valid_if_blocks.rs:133:23 | LL | } else if x == 68 { | _______________________^ @@ -66,7 +66,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:137:12 + --> $DIR/valid_if_blocks.rs:138:12 | LL | } else { | ____________^ @@ -78,7 +78,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:146:23 + --> $DIR/valid_if_blocks.rs:147:23 | LL | } else if x == 68 { | _______________________^ @@ -88,7 +88,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:149:12 + --> $DIR/valid_if_blocks.rs:150:12 | LL | } else { | ____________^ diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index 7ecefd7b1343..a37f3fec20f1 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::cast_abs_to_unsigned)] +#![allow(clippy::uninlined_format_args)] fn main() { let x: i32 = -42; diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 30c603fca9a1..5706930af5a0 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::cast_abs_to_unsigned)] +#![allow(clippy::uninlined_format_args)] fn main() { let x: i32 = -42; diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 045537745267..7cea11c183d2 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:6:18 + --> $DIR/cast_abs_to_unsigned.rs:7:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,97 +7,97 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:10:20 + --> $DIR/cast_abs_to_unsigned.rs:11:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:11:20 + --> $DIR/cast_abs_to_unsigned.rs:12:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:12:13 + --> $DIR/cast_abs_to_unsigned.rs:13:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:15:13 + --> $DIR/cast_abs_to_unsigned.rs:16:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:16:13 + --> $DIR/cast_abs_to_unsigned.rs:17:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:17:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:23:13 + --> $DIR/cast_abs_to_unsigned.rs:24:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:24:13 + --> $DIR/cast_abs_to_unsigned.rs:25:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:25:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:30:13 + --> $DIR/cast_abs_to_unsigned.rs:31:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 603ae7dc9eb1..7d53e08345d3 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -1,9 +1,10 @@ #![warn(clippy::collapsible_match)] #![allow( + clippy::equatable_if_let, clippy::needless_return, clippy::no_effect, clippy::single_match, - clippy::equatable_if_let + clippy::uninlined_format_args )] fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 33562e8401ca..2580bef58091 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -1,5 +1,5 @@ error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:12:20 + --> $DIR/collapsible_match.rs:13:20 | LL | Ok(val) => match val { | ____________________^ @@ -9,7 +9,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:12:12 + --> $DIR/collapsible_match.rs:13:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -18,7 +18,7 @@ LL | Some(n) => foo(n), = note: `-D clippy::collapsible-match` implied by `-D warnings` error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:21:20 + --> $DIR/collapsible_match.rs:22:20 | LL | Ok(val) => match val { | ____________________^ @@ -28,7 +28,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:21:12 + --> $DIR/collapsible_match.rs:22:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -36,7 +36,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:30:9 + --> $DIR/collapsible_match.rs:31:9 | LL | / if let Some(n) = val { LL | | take(n); @@ -44,7 +44,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:29:15 + --> $DIR/collapsible_match.rs:30:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -52,7 +52,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:37:9 + --> $DIR/collapsible_match.rs:38:9 | LL | / if let Some(n) = val { LL | | take(n); @@ -62,7 +62,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:36:15 + --> $DIR/collapsible_match.rs:37:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -70,7 +70,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:48:9 + --> $DIR/collapsible_match.rs:49:9 | LL | / match val { LL | | Some(n) => foo(n), @@ -79,7 +79,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:47:15 + --> $DIR/collapsible_match.rs:48:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -88,7 +88,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:57:13 + --> $DIR/collapsible_match.rs:58:13 | LL | / if let Some(n) = val { LL | | take(n); @@ -96,7 +96,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:56:12 + --> $DIR/collapsible_match.rs:57:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -104,7 +104,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `if let` - --> $DIR/collapsible_match.rs:66:9 + --> $DIR/collapsible_match.rs:67:9 | LL | / match val { LL | | Some(n) => foo(n), @@ -113,7 +113,7 @@ LL | | } | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:65:15 + --> $DIR/collapsible_match.rs:66:15 | LL | if let Ok(val) = res_opt { | ^^^ replace this binding @@ -122,7 +122,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `if let` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:77:13 + --> $DIR/collapsible_match.rs:78:13 | LL | / if let Some(n) = val { LL | | take(n); @@ -132,7 +132,7 @@ LL | | } | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:76:12 + --> $DIR/collapsible_match.rs:77:12 | LL | Ok(val) => { | ^^^ replace this binding @@ -140,7 +140,7 @@ LL | if let Some(n) = val { | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:88:20 + --> $DIR/collapsible_match.rs:89:20 | LL | Ok(val) => match val { | ____________________^ @@ -150,7 +150,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:88:12 + --> $DIR/collapsible_match.rs:89:12 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -158,7 +158,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: this `match` can be collapsed into the outer `match` - --> $DIR/collapsible_match.rs:97:22 + --> $DIR/collapsible_match.rs:98:22 | LL | Some(val) => match val { | ______________________^ @@ -168,7 +168,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match.rs:97:14 + --> $DIR/collapsible_match.rs:98:14 | LL | Some(val) => match val { | ^^^ replace this binding diff --git a/tests/ui/crashes/ice-4775.rs b/tests/ui/crashes/ice-4775.rs index 405e3039e7d0..f693aafd1cbb 100644 --- a/tests/ui/crashes/ice-4775.rs +++ b/tests/ui/crashes/ice-4775.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + pub struct ArrayWrapper([usize; N]); impl ArrayWrapper<{ N }> { diff --git a/tests/ui/crashes/ice-9445.rs b/tests/ui/crashes/ice-9445.rs new file mode 100644 index 000000000000..c67b22f6f8c4 --- /dev/null +++ b/tests/ui/crashes/ice-9445.rs @@ -0,0 +1,3 @@ +const UNINIT: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); + +fn main() {} diff --git a/tests/ui/crashes/ice-9459.rs b/tests/ui/crashes/ice-9459.rs new file mode 100644 index 000000000000..55615124fcf0 --- /dev/null +++ b/tests/ui/crashes/ice-9459.rs @@ -0,0 +1,5 @@ +#![feature(unsized_fn_params)] + +pub fn f0(_f: dyn FnOnce()) {} + +fn main() {} diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index 55a8b403407c..b34997d4ee0b 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -1,4 +1,4 @@ -#![allow(clippy::disallowed_names)] +#![allow(clippy::disallowed_names, clippy::uninlined_format_args)] pub fn foo(bar: *const u8) { println!("{:#p}", bar); diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index fce66eb17596..eedd43619392 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -1,8 +1,8 @@ // run-rustfix // aux-build: proc_macro_with_span.rs - -#![allow(unused_imports, dead_code)] #![deny(clippy::default_trait_access)] +#![allow(dead_code, unused_imports)] +#![allow(clippy::uninlined_format_args)] extern crate proc_macro_with_span; diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index 3e8e898b7bc6..11d4bc5c5f02 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,8 +1,8 @@ // run-rustfix // aux-build: proc_macro_with_span.rs - -#![allow(unused_imports, dead_code)] #![deny(clippy::default_trait_access)] +#![allow(dead_code, unused_imports)] +#![allow(clippy::uninlined_format_args)] extern crate proc_macro_with_span; diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 3493de37a55b..49b2dde3f1e8 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -5,7 +5,7 @@ LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` | note: the lint level is defined here - --> $DIR/default_trait_access.rs:5:9 + --> $DIR/default_trait_access.rs:3:9 | LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/doc_link_with_quotes.rs b/tests/ui/doc_link_with_quotes.rs index ab52fb1a4a69..17c04c34e246 100644 --- a/tests/ui/doc_link_with_quotes.rs +++ b/tests/ui/doc_link_with_quotes.rs @@ -4,9 +4,14 @@ fn main() { foo() } -/// Calls ['bar'] +/// Calls ['bar'] uselessly pub fn foo() { bar() } +/// # Examples +/// This demonstrates issue \#8961 +/// ``` +/// let _ = vec!['w', 'a', 't']; +/// ``` pub fn bar() {} diff --git a/tests/ui/doc_link_with_quotes.stderr b/tests/ui/doc_link_with_quotes.stderr index bf6d57d8afe8..ea730e667d65 100644 --- a/tests/ui/doc_link_with_quotes.stderr +++ b/tests/ui/doc_link_with_quotes.stderr @@ -1,8 +1,8 @@ error: possible intra-doc link using quotes instead of backticks - --> $DIR/doc_link_with_quotes.rs:7:1 + --> $DIR/doc_link_with_quotes.rs:7:12 | -LL | /// Calls ['bar'] - | ^^^^^^^^^^^^^^^^^ +LL | /// Calls ['bar'] uselessly + | ^^^^^ | = note: `-D clippy::doc-link-with-quotes` implied by `-D warnings` diff --git a/tests/ui/drop_forget_copy.rs b/tests/ui/drop_forget_copy.rs index 7c7a9ecff67f..a7276dd59f43 100644 --- a/tests/ui/drop_forget_copy.rs +++ b/tests/ui/drop_forget_copy.rs @@ -64,3 +64,23 @@ fn main() { let a5 = a1.clone(); forget(a5); } + +#[allow(unused)] +#[allow(clippy::unit_cmp)] +fn issue9482(x: u8) { + fn println_and(t: T) -> T { + println!("foo"); + t + } + + match x { + 0 => drop(println_and(12)), // Don't lint (copy type), we only care about side-effects + 1 => drop(println_and(String::new())), // Don't lint (no copy type), we only care about side-effects + 2 => { + drop(println_and(13)); // Lint, even if we only care about the side-effect, it's already in a block + }, + 3 if drop(println_and(14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + 4 => drop(2), // Lint, not a fn/method call + _ => (), + } +} diff --git a/tests/ui/drop_forget_copy.stderr b/tests/ui/drop_forget_copy.stderr index 21adb3b3a504..90bef1c3c439 100644 --- a/tests/ui/drop_forget_copy.stderr +++ b/tests/ui/drop_forget_copy.stderr @@ -72,5 +72,41 @@ note: argument has type `SomeStruct` LL | forget(s4); | ^^ -error: aborting due to 6 previous errors +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact + --> $DIR/drop_forget_copy.rs:80:13 + | +LL | drop(println_and(13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `i32` + --> $DIR/drop_forget_copy.rs:80:18 + | +LL | drop(println_and(13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact + --> $DIR/drop_forget_copy.rs:82:14 + | +LL | 3 if drop(println_and(14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `i32` + --> $DIR/drop_forget_copy.rs:82:19 + | +LL | 3 if drop(println_and(14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact + --> $DIR/drop_forget_copy.rs:83:14 + | +LL | 4 => drop(2), // Lint, not a fn/method call + | ^^^^^^^ + | +note: argument has type `i32` + --> $DIR/drop_forget_copy.rs:83:19 + | +LL | 4 => drop(2), // Lint, not a fn/method call + | ^ + +error: aborting due to 9 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index f8d559bf226f..a9cc80aaaf62 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -1,14 +1,14 @@ // run-rustfix - +#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)] +#![allow(unused)] #![allow( - unused, - clippy::no_effect, - clippy::redundant_closure_call, + clippy::needless_borrow, clippy::needless_pass_by_value, + clippy::no_effect, clippy::option_map_unit_fn, - clippy::needless_borrow + clippy::redundant_closure_call, + clippy::uninlined_format_args )] -#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)] use std::path::{Path, PathBuf}; @@ -303,3 +303,16 @@ fn not_general_enough() { fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {} f(|path| std::fs::remove_file(path)); } + +// https://github.com/rust-lang/rust-clippy/issues/9369 +pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -> impl FnMut() { + fn takes_fn_mut(_: impl FnMut()) {} + takes_fn_mut(&mut f); + + fn takes_fn_once(_: impl FnOnce()) {} + takes_fn_once(&mut f); + + f(); + + move || takes_fn_mut(&mut f_used_once) +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index f0fb55a1e5f0..cc99906ccd66 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -1,14 +1,14 @@ // run-rustfix - +#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)] +#![allow(unused)] #![allow( - unused, - clippy::no_effect, - clippy::redundant_closure_call, + clippy::needless_borrow, clippy::needless_pass_by_value, + clippy::no_effect, clippy::option_map_unit_fn, - clippy::needless_borrow + clippy::redundant_closure_call, + clippy::uninlined_format_args )] -#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)] use std::path::{Path, PathBuf}; @@ -303,3 +303,16 @@ fn not_general_enough() { fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {} f(|path| std::fs::remove_file(path)); } + +// https://github.com/rust-lang/rust-clippy/issues/9369 +pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -> impl FnMut() { + fn takes_fn_mut(_: impl FnMut()) {} + takes_fn_mut(|| f()); + + fn takes_fn_once(_: impl FnOnce()) {} + takes_fn_once(|| f()); + + f(); + + move || takes_fn_mut(|| f_used_once()) +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index bf2e97e744ab..434706b7e258 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -116,5 +116,23 @@ error: redundant closure LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` -error: aborting due to 19 previous errors +error: redundant closure + --> $DIR/eta.rs:310:18 + | +LL | takes_fn_mut(|| f()); + | ^^^^^^ help: replace the closure with the function itself: `&mut f` + +error: redundant closure + --> $DIR/eta.rs:313:19 + | +LL | takes_fn_once(|| f()); + | ^^^^^^ help: replace the closure with the function itself: `&mut f` + +error: redundant closure + --> $DIR/eta.rs:317:26 + | +LL | move || takes_fn_mut(|| f_used_once()) + | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` + +error: aborting due to 22 previous errors diff --git a/tests/ui/expect_fun_call.fixed b/tests/ui/expect_fun_call.fixed index 53e45d28bded..15172ae345c2 100644 --- a/tests/ui/expect_fun_call.fixed +++ b/tests/ui/expect_fun_call.fixed @@ -1,7 +1,6 @@ // run-rustfix - #![warn(clippy::expect_fun_call)] -#![allow(clippy::to_string_in_format_args)] +#![allow(clippy::to_string_in_format_args, clippy::uninlined_format_args)] /// Checks implementation of the `EXPECT_FUN_CALL` lint @@ -101,4 +100,10 @@ fn main() { let opt_ref = &opt; opt_ref.unwrap_or_else(|| panic!("{:?}", opt_ref)); } + + let format_capture: Option = None; + format_capture.unwrap_or_else(|| panic!("{error_code}")); + + let format_capture_and_value: Option = None; + format_capture_and_value.unwrap_or_else(|| panic!("{error_code}, {}", 1)); } diff --git a/tests/ui/expect_fun_call.rs b/tests/ui/expect_fun_call.rs index 22e530b80349..0f448d004174 100644 --- a/tests/ui/expect_fun_call.rs +++ b/tests/ui/expect_fun_call.rs @@ -1,7 +1,6 @@ // run-rustfix - #![warn(clippy::expect_fun_call)] -#![allow(clippy::to_string_in_format_args)] +#![allow(clippy::to_string_in_format_args, clippy::uninlined_format_args)] /// Checks implementation of the `EXPECT_FUN_CALL` lint @@ -101,4 +100,10 @@ fn main() { let opt_ref = &opt; opt_ref.expect(&format!("{:?}", opt_ref)); } + + let format_capture: Option = None; + format_capture.expect(&format!("{error_code}")); + + let format_capture_and_value: Option = None; + format_capture_and_value.expect(&format!("{error_code}, {}", 1)); } diff --git a/tests/ui/expect_fun_call.stderr b/tests/ui/expect_fun_call.stderr index aca15935fca0..cb55e32aee02 100644 --- a/tests/ui/expect_fun_call.stderr +++ b/tests/ui/expect_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:35:26 + --> $DIR/expect_fun_call.rs:34:26 | LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` @@ -7,76 +7,88 @@ LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code = note: `-D clippy::expect-fun-call` implied by `-D warnings` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:38:26 + --> $DIR/expect_fun_call.rs:37:26 | LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:41:37 + --> $DIR/expect_fun_call.rs:40:37 | LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:51:25 + --> $DIR/expect_fun_call.rs:50:25 | LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:54:25 + --> $DIR/expect_fun_call.rs:53:25 | LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:66:17 + --> $DIR/expect_fun_call.rs:65:17 | LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:87:21 + --> $DIR/expect_fun_call.rs:86:21 | LL | Some("foo").expect(&get_string()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:88:21 + --> $DIR/expect_fun_call.rs:87:21 | LL | Some("foo").expect(get_string().as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:89:21 + --> $DIR/expect_fun_call.rs:88:21 | LL | Some("foo").expect(get_string().as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:91:21 + --> $DIR/expect_fun_call.rs:90:21 | LL | Some("foo").expect(get_static_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:92:21 + --> $DIR/expect_fun_call.rs:91:21 | LL | Some("foo").expect(get_non_static_str(&0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:96:16 + --> $DIR/expect_fun_call.rs:95:16 | LL | Some(true).expect(&format!("key {}, {}", 1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` error: use of `expect` followed by a function call - --> $DIR/expect_fun_call.rs:102:17 + --> $DIR/expect_fun_call.rs:101:17 | LL | opt_ref.expect(&format!("{:?}", opt_ref)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))` -error: aborting due to 13 previous errors +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:105:20 + | +LL | format_capture.expect(&format!("{error_code}")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{error_code}"))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:108:30 + | +LL | format_capture_and_value.expect(&format!("{error_code}, {}", 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{error_code}, {}", 1))` + +error: aborting due to 15 previous errors diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index aa966761febd..6eddc01e2c47 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -1,4 +1,5 @@ #![warn(clippy::explicit_counter_loop)] +#![allow(clippy::uninlined_format_args)] fn main() { let mut vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/explicit_counter_loop.stderr b/tests/ui/explicit_counter_loop.stderr index f9f8407d5775..d3f3c626bbdf 100644 --- a/tests/ui/explicit_counter_loop.stderr +++ b/tests/ui/explicit_counter_loop.stderr @@ -1,5 +1,5 @@ error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:6:5 + --> $DIR/explicit_counter_loop.rs:7:5 | LL | for _v in &vec { | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` @@ -7,49 +7,49 @@ LL | for _v in &vec { = note: `-D clippy::explicit-counter-loop` implied by `-D warnings` error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:12:5 + --> $DIR/explicit_counter_loop.rs:13:5 | LL | for _v in &vec { | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:17:5 + --> $DIR/explicit_counter_loop.rs:18:5 | LL | for _v in &mut vec { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter_mut().enumerate()` error: the variable `_index` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:22:5 + --> $DIR/explicit_counter_loop.rs:23:5 | LL | for _v in vec { | ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()` error: the variable `count` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:61:9 + --> $DIR/explicit_counter_loop.rs:62:9 | LL | for ch in text.chars() { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` error: the variable `count` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:72:9 + --> $DIR/explicit_counter_loop.rs:73:9 | LL | for ch in text.chars() { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` error: the variable `count` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:130:9 + --> $DIR/explicit_counter_loop.rs:131:9 | LL | for _i in 3..10 { | ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()` error: the variable `idx_usize` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:170:9 + --> $DIR/explicit_counter_loop.rs:171:9 | LL | for _item in slice { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_usize, _item) in slice.iter().enumerate()` error: the variable `idx_u32` is used as a loop counter - --> $DIR/explicit_counter_loop.rs:182:9 + --> $DIR/explicit_counter_loop.rs:183:9 | LL | for _item in slice { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_u32, _item) in (0_u32..).zip(slice.iter())` diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 523cae183ee6..6d32bbece1e5 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -1,13 +1,13 @@ // run-rustfix - +#![warn(clippy::explicit_deref_methods)] +#![allow(unused_variables)] #![allow( - unused_variables, + clippy::borrow_deref_ref, clippy::clone_double_ref, + clippy::explicit_auto_deref, clippy::needless_borrow, - clippy::borrow_deref_ref, - clippy::explicit_auto_deref + clippy::uninlined_format_args )] -#![warn(clippy::explicit_deref_methods)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index 0bbc1ae57cdf..779909e42380 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -1,13 +1,13 @@ // run-rustfix - +#![warn(clippy::explicit_deref_methods)] +#![allow(unused_variables)] #![allow( - unused_variables, + clippy::borrow_deref_ref, clippy::clone_double_ref, + clippy::explicit_auto_deref, clippy::needless_borrow, - clippy::borrow_deref_ref, - clippy::explicit_auto_deref + clippy::uninlined_format_args )] -#![warn(clippy::explicit_deref_methods)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_write.fixed b/tests/ui/explicit_write.fixed index 35283725619a..862c3fea9ee8 100644 --- a/tests/ui/explicit_write.fixed +++ b/tests/ui/explicit_write.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports)] #![warn(clippy::explicit_write)] +#![allow(unused_imports)] +#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() diff --git a/tests/ui/explicit_write.rs b/tests/ui/explicit_write.rs index be864a55b663..41d7c2255738 100644 --- a/tests/ui/explicit_write.rs +++ b/tests/ui/explicit_write.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports)] #![warn(clippy::explicit_write)] +#![allow(unused_imports)] +#![allow(clippy::uninlined_format_args)] fn stdout() -> String { String::new() diff --git a/tests/ui/explicit_write.stderr b/tests/ui/explicit_write.stderr index ff05f4343d77..457e9c627180 100644 --- a/tests/ui/explicit_write.stderr +++ b/tests/ui/explicit_write.stderr @@ -1,5 +1,5 @@ error: use of `write!(stdout(), ...).unwrap()` - --> $DIR/explicit_write.rs:23:9 + --> $DIR/explicit_write.rs:24:9 | LL | write!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` @@ -7,73 +7,73 @@ LL | write!(std::io::stdout(), "test").unwrap(); = note: `-D clippy::explicit-write` implied by `-D warnings` error: use of `write!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:24:9 + --> $DIR/explicit_write.rs:25:9 | LL | write!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> $DIR/explicit_write.rs:25:9 + --> $DIR/explicit_write.rs:26:9 | LL | writeln!(std::io::stdout(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:26:9 + --> $DIR/explicit_write.rs:27:9 | LL | writeln!(std::io::stderr(), "test").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")` error: use of `stdout().write_fmt(...).unwrap()` - --> $DIR/explicit_write.rs:27:9 + --> $DIR/explicit_write.rs:28:9 | LL | std::io::stdout().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` error: use of `stderr().write_fmt(...).unwrap()` - --> $DIR/explicit_write.rs:28:9 + --> $DIR/explicit_write.rs:29:9 | LL | std::io::stderr().write_fmt(format_args!("test")).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` error: use of `writeln!(stdout(), ...).unwrap()` - --> $DIR/explicit_write.rs:31:9 + --> $DIR/explicit_write.rs:32:9 | LL | writeln!(std::io::stdout(), "test/ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:32:9 + --> $DIR/explicit_write.rs:33:9 | LL | writeln!(std::io::stderr(), "test/ntest").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:35:9 + --> $DIR/explicit_write.rs:36:9 | LL | writeln!(std::io::stderr(), "with {}", value).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {}", value)` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:36:9 + --> $DIR/explicit_write.rs:37:9 | LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {} {}", 2, value)` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:37:9 + --> $DIR/explicit_write.rs:38:9 | LL | writeln!(std::io::stderr(), "with {value}").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {value}")` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:38:9 + --> $DIR/explicit_write.rs:39:9 | LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())` error: use of `writeln!(stderr(), ...).unwrap()` - --> $DIR/explicit_write.rs:40:9 + --> $DIR/explicit_write.rs:41:9 | LL | writeln!(std::io::stderr(), "{:w$}", value, w = width).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("{:w$}", value, w = width)` diff --git a/tests/ui/fallible_impl_from.rs b/tests/ui/fallible_impl_from.rs index 5d5af4e46329..fb6e8ec706b1 100644 --- a/tests/ui/fallible_impl_from.rs +++ b/tests/ui/fallible_impl_from.rs @@ -1,4 +1,5 @@ #![deny(clippy::fallible_impl_from)] +#![allow(clippy::uninlined_format_args)] // docs example struct Foo(i32); diff --git a/tests/ui/fallible_impl_from.stderr b/tests/ui/fallible_impl_from.stderr index 28a061af664d..21761484f8c4 100644 --- a/tests/ui/fallible_impl_from.stderr +++ b/tests/ui/fallible_impl_from.stderr @@ -1,5 +1,5 @@ error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:5:1 + --> $DIR/fallible_impl_from.rs:6:1 | LL | / impl From for Foo { LL | | fn from(s: String) -> Self { @@ -10,7 +10,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:7:13 + --> $DIR/fallible_impl_from.rs:8:13 | LL | Foo(s.parse().unwrap()) | ^^^^^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | #![deny(clippy::fallible_impl_from)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:26:1 + --> $DIR/fallible_impl_from.rs:27:1 | LL | / impl From for Invalid { LL | | fn from(i: usize) -> Invalid { @@ -34,14 +34,14 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:29:13 + --> $DIR/fallible_impl_from.rs:30:13 | LL | panic!(); | ^^^^^^^^ = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:35:1 + --> $DIR/fallible_impl_from.rs:36:1 | LL | / impl From> for Invalid { LL | | fn from(s: Option) -> Invalid { @@ -54,7 +54,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:37:17 + --> $DIR/fallible_impl_from.rs:38:17 | LL | let s = s.unwrap(); | ^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | panic!("{:?}", s); = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: consider implementing `TryFrom` instead - --> $DIR/fallible_impl_from.rs:53:1 + --> $DIR/fallible_impl_from.rs:54:1 | LL | / impl<'a> From<&'a mut as ProjStrTrait>::ProjString> for Invalid { LL | | fn from(s: &'a mut as ProjStrTrait>::ProjString) -> Invalid { @@ -81,7 +81,7 @@ LL | | } | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) - --> $DIR/fallible_impl_from.rs:55:12 + --> $DIR/fallible_impl_from.rs:56:12 | LL | if s.parse::().ok().unwrap() != 42 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/floating_point_exp.fixed b/tests/ui/floating_point_exp.fixed index c86a502d15f0..b9e3d89c2b29 100644 --- a/tests/ui/floating_point_exp.fixed +++ b/tests/ui/floating_point_exp.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 2f32; diff --git a/tests/ui/floating_point_exp.rs b/tests/ui/floating_point_exp.rs index e59589f912a2..ef008dd9be05 100644 --- a/tests/ui/floating_point_exp.rs +++ b/tests/ui/floating_point_exp.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 2f32; diff --git a/tests/ui/floating_point_exp.stderr b/tests/ui/floating_point_exp.stderr index f84eede19872..b92fae56e421 100644 --- a/tests/ui/floating_point_exp.stderr +++ b/tests/ui/floating_point_exp.stderr @@ -1,5 +1,5 @@ error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:6:13 + --> $DIR/floating_point_exp.rs:7:13 | LL | let _ = x.exp() - 1.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` @@ -7,25 +7,25 @@ LL | let _ = x.exp() - 1.0; = note: `-D clippy::imprecise-flops` implied by `-D warnings` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:7:13 + --> $DIR/floating_point_exp.rs:8:13 | LL | let _ = x.exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:8:13 + --> $DIR/floating_point_exp.rs:9:13 | LL | let _ = (x as f32).exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:14:13 + --> $DIR/floating_point_exp.rs:15:13 | LL | let _ = x.exp() - 1.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:15:13 + --> $DIR/floating_point_exp.rs:16:13 | LL | let _ = x.exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 4def9300bb7d..ee5406461600 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(dead_code, clippy::double_parens)] +#![allow(dead_code, clippy::double_parens, clippy::unnecessary_cast)] #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] const TWO: f32 = 2.0; diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index 1e04caa7d2a8..0590670a50bc 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(dead_code, clippy::double_parens)] +#![allow(dead_code, clippy::double_parens, clippy::unnecessary_cast)] #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] const TWO: f32 = 2.0; diff --git a/tests/ui/floating_point_logbase.fixed b/tests/ui/floating_point_logbase.fixed index 936462f94066..7347bf72cbea 100644 --- a/tests/ui/floating_point_logbase.fixed +++ b/tests/ui/floating_point_logbase.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_logbase.rs b/tests/ui/floating_point_logbase.rs index 0b56fa8fa41f..ba5b8d406928 100644 --- a/tests/ui/floating_point_logbase.rs +++ b/tests/ui/floating_point_logbase.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_logbase.stderr b/tests/ui/floating_point_logbase.stderr index 384e3554cbbe..9d736b5e1a27 100644 --- a/tests/ui/floating_point_logbase.stderr +++ b/tests/ui/floating_point_logbase.stderr @@ -1,5 +1,5 @@ error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:7:13 + --> $DIR/floating_point_logbase.rs:8:13 | LL | let _ = x.ln() / y.ln(); | ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` @@ -7,25 +7,25 @@ LL | let _ = x.ln() / y.ln(); = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:8:13 + --> $DIR/floating_point_logbase.rs:9:13 | LL | let _ = (x as f32).ln() / y.ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:9:13 + --> $DIR/floating_point_logbase.rs:10:13 | LL | let _ = x.log2() / y.log2(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:10:13 + --> $DIR/floating_point_logbase.rs:11:13 | LL | let _ = x.log10() / y.log10(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:11:13 + --> $DIR/floating_point_logbase.rs:12:13 | LL | let _ = x.log(5f32) / y.log(5f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed index 169ec02f82be..d3e536ba3500 100644 --- a/tests/ui/floating_point_mul_add.fixed +++ b/tests/ui/floating_point_mul_add.fixed @@ -19,7 +19,9 @@ fn main() { let d: f64 = 0.0001; let _ = a.mul_add(b, c); + let _ = a.mul_add(b, -c); let _ = a.mul_add(b, c); + let _ = a.mul_add(-b, c); let _ = 2.0f64.mul_add(4.0, a); let _ = 2.0f64.mul_add(4., a); diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs index 5338d4fc2b74..5d4a9e35cfc2 100644 --- a/tests/ui/floating_point_mul_add.rs +++ b/tests/ui/floating_point_mul_add.rs @@ -19,7 +19,9 @@ fn main() { let d: f64 = 0.0001; let _ = a * b + c; + let _ = a * b - c; let _ = c + a * b; + let _ = c - a * b; let _ = a + 2.0 * 4.0; let _ = a + 2. * 4.; diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index e637bbf90caa..a79ae94e8d43 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr @@ -9,56 +9,68 @@ LL | let _ = a * b + c; error: multiply and add expressions can be calculated more efficiently and accurately --> $DIR/floating_point_mul_add.rs:22:13 | +LL | let _ = a * b - c; + | ^^^^^^^^^ help: consider using: `a.mul_add(b, -c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:23:13 + | LL | let _ = c + a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:23:13 + --> $DIR/floating_point_mul_add.rs:24:13 + | +LL | let _ = c - a * b; + | ^^^^^^^^^ help: consider using: `a.mul_add(-b, c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:25:13 | LL | let _ = a + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:24:13 + --> $DIR/floating_point_mul_add.rs:26:13 | LL | let _ = a + 2. * 4.; | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:26:13 + --> $DIR/floating_point_mul_add.rs:28:13 | LL | let _ = (a * b) + c; | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:27:13 + --> $DIR/floating_point_mul_add.rs:29:13 | LL | let _ = c + (a * b); | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:28:13 + --> $DIR/floating_point_mul_add.rs:30:13 | LL | let _ = a * b * c + d; | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:30:13 + --> $DIR/floating_point_mul_add.rs:32:13 | LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:31:13 + --> $DIR/floating_point_mul_add.rs:33:13 | LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_mul_add.rs:33:13 + --> $DIR/floating_point_mul_add.rs:35:13 | LL | let _ = (a * a + b).sqrt(); | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)` -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/floating_point_powf.fixed b/tests/ui/floating_point_powf.fixed index e7ef45634dff..f7f93de29577 100644 --- a/tests/ui/floating_point_powf.fixed +++ b/tests/ui/floating_point_powf.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_powf.rs b/tests/ui/floating_point_powf.rs index d749aa2d48a4..499fc0e15e47 100644 --- a/tests/ui/floating_point_powf.rs +++ b/tests/ui/floating_point_powf.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_powf.stderr b/tests/ui/floating_point_powf.stderr index e9693de8fc90..7c9d50db2f78 100644 --- a/tests/ui/floating_point_powf.stderr +++ b/tests/ui/floating_point_powf.stderr @@ -1,5 +1,5 @@ error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:6:13 + --> $DIR/floating_point_powf.rs:7:13 | LL | let _ = 2f32.powf(x); | ^^^^^^^^^^^^ help: consider using: `x.exp2()` @@ -7,43 +7,43 @@ LL | let _ = 2f32.powf(x); = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:7:13 + --> $DIR/floating_point_powf.rs:8:13 | LL | let _ = 2f32.powf(3.1); | ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:8:13 + --> $DIR/floating_point_powf.rs:9:13 | LL | let _ = 2f32.powf(-3.1); | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:9:13 + --> $DIR/floating_point_powf.rs:10:13 | LL | let _ = std::f32::consts::E.powf(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:10:13 + --> $DIR/floating_point_powf.rs:11:13 | LL | let _ = std::f32::consts::E.powf(3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:11:13 + --> $DIR/floating_point_powf.rs:12:13 | LL | let _ = std::f32::consts::E.powf(-3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()` error: square-root of a number can be computed more efficiently and accurately - --> $DIR/floating_point_powf.rs:12:13 + --> $DIR/floating_point_powf.rs:13:13 | LL | let _ = x.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:13:13 + --> $DIR/floating_point_powf.rs:14:13 | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` @@ -51,139 +51,139 @@ LL | let _ = x.powf(1.0 / 3.0); = note: `-D clippy::imprecise-flops` implied by `-D warnings` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:14:13 + --> $DIR/floating_point_powf.rs:15:13 | LL | let _ = (x as f32).powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).cbrt()` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:15:13 + --> $DIR/floating_point_powf.rs:16:13 | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:16:13 + --> $DIR/floating_point_powf.rs:17:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:17:13 + --> $DIR/floating_point_powf.rs:18:13 | LL | let _ = x.powf(16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:18:13 + --> $DIR/floating_point_powf.rs:19:13 | LL | let _ = x.powf(-16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:19:13 + --> $DIR/floating_point_powf.rs:20:13 | LL | let _ = (x as f32).powf(-16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(-16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:20:13 + --> $DIR/floating_point_powf.rs:21:13 | LL | let _ = (x as f32).powf(3.0); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(3)` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:21:13 + --> $DIR/floating_point_powf.rs:22:13 | LL | let _ = (1.5_f32 + 1.0).powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(1.5_f32 + 1.0).cbrt()` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:22:13 + --> $DIR/floating_point_powf.rs:23:13 | LL | let _ = 1.5_f64.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.cbrt()` error: square-root of a number can be computed more efficiently and accurately - --> $DIR/floating_point_powf.rs:23:13 + --> $DIR/floating_point_powf.rs:24:13 | LL | let _ = 1.5_f64.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.sqrt()` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:24:13 + --> $DIR/floating_point_powf.rs:25:13 | LL | let _ = 1.5_f64.powf(3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `1.5_f64.powi(3)` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:33:13 + --> $DIR/floating_point_powf.rs:34:13 | LL | let _ = 2f64.powf(x); | ^^^^^^^^^^^^ help: consider using: `x.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:34:13 + --> $DIR/floating_point_powf.rs:35:13 | LL | let _ = 2f64.powf(3.1); | ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:35:13 + --> $DIR/floating_point_powf.rs:36:13 | LL | let _ = 2f64.powf(-3.1); | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:36:13 + --> $DIR/floating_point_powf.rs:37:13 | LL | let _ = std::f64::consts::E.powf(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:37:13 + --> $DIR/floating_point_powf.rs:38:13 | LL | let _ = std::f64::consts::E.powf(3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:38:13 + --> $DIR/floating_point_powf.rs:39:13 | LL | let _ = std::f64::consts::E.powf(-3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()` error: square-root of a number can be computed more efficiently and accurately - --> $DIR/floating_point_powf.rs:39:13 + --> $DIR/floating_point_powf.rs:40:13 | LL | let _ = x.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:40:13 + --> $DIR/floating_point_powf.rs:41:13 | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:41:13 + --> $DIR/floating_point_powf.rs:42:13 | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:42:13 + --> $DIR/floating_point_powf.rs:43:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:43:13 + --> $DIR/floating_point_powf.rs:44:13 | LL | let _ = x.powf(-2_147_483_648.0); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:44:13 + --> $DIR/floating_point_powf.rs:45:13 | LL | let _ = x.powf(2_147_483_647.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)` diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 5758db7c6c82..884d05fed71b 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let one = 1; @@ -7,7 +8,9 @@ fn main() { let y = 4f32; let _ = x.mul_add(x, y); + let _ = x.mul_add(x, -y); let _ = y.mul_add(y, x); + let _ = y.mul_add(-y, x); let _ = (y as f32).mul_add(y as f32, x); let _ = x.mul_add(x, y).sqrt(); let _ = y.mul_add(y, x).sqrt(); diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index 5926bf1b0004..e6a1c895371b 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::suboptimal_flops)] +#![allow(clippy::unnecessary_cast)] fn main() { let one = 1; @@ -7,7 +8,9 @@ fn main() { let y = 4f32; let _ = x.powi(2) + y; + let _ = x.powi(2) - y; let _ = x + y.powi(2); + let _ = x - y.powi(2); let _ = x + (y as f32).powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index a3c74544212b..5df0de1fef22 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -1,5 +1,5 @@ error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:9:13 + --> $DIR/floating_point_powi.rs:10:13 | LL | let _ = x.powi(2) + y; | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` @@ -7,28 +7,40 @@ LL | let _ = x.powi(2) + y; = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:10:13 + --> $DIR/floating_point_powi.rs:11:13 + | +LL | let _ = x.powi(2) - y; + | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:12:13 | LL | let _ = x + y.powi(2); | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:11:13 + --> $DIR/floating_point_powi.rs:13:13 + | +LL | let _ = x - y.powi(2); + | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(-y, x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:14:13 | LL | let _ = x + (y as f32).powi(2); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y as f32).mul_add(y as f32, x)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:12:13 + --> $DIR/floating_point_powi.rs:15:13 | LL | let _ = (x.powi(2) + y).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:13:13 + --> $DIR/floating_point_powi.rs:16:13 | LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index aa69781d15a6..e9dd38fe40e6 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -1,6 +1,6 @@ // run-rustfix - #![allow(dead_code, unused)] +#![allow(clippy::uninlined_format_args)] use std::collections::*; diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 7c063d99511d..534fb4dd4ef2 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -1,6 +1,6 @@ // run-rustfix - #![allow(dead_code, unused)] +#![allow(clippy::uninlined_format_args)] use std::collections::*; diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 3390111d0a8f..4b2a9297d084 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -1,4 +1,5 @@ #![warn(clippy::for_loops_over_fallibles)] +#![allow(clippy::uninlined_format_args)] fn for_loops_over_fallibles() { let option = Some(1); diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr index 68d2735b040e..f09adccabd1a 100644 --- a/tests/ui/for_loops_over_fallibles.stderr +++ b/tests/ui/for_loops_over_fallibles.stderr @@ -1,5 +1,5 @@ error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:9:14 + --> $DIR/for_loops_over_fallibles.rs:10:14 | LL | for x in option { | ^^^^^^ @@ -8,7 +8,7 @@ LL | for x in option { = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:14:14 + --> $DIR/for_loops_over_fallibles.rs:15:14 | LL | for x in option.iter() { | ^^^^^^ @@ -16,7 +16,7 @@ LL | for x in option.iter() { = help: consider replacing `for x in option.iter()` with `if let Some(x) = option` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:19:14 + --> $DIR/for_loops_over_fallibles.rs:20:14 | LL | for x in result { | ^^^^^^ @@ -24,7 +24,7 @@ LL | for x in result { = help: consider replacing `for x in result` with `if let Ok(x) = result` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:24:14 + --> $DIR/for_loops_over_fallibles.rs:25:14 | LL | for x in result.iter_mut() { | ^^^^^^ @@ -32,7 +32,7 @@ LL | for x in result.iter_mut() { = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:29:14 + --> $DIR/for_loops_over_fallibles.rs:30:14 | LL | for x in result.into_iter() { | ^^^^^^ @@ -40,7 +40,7 @@ LL | for x in result.into_iter() { = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:33:14 + --> $DIR/for_loops_over_fallibles.rs:34:14 | LL | for x in option.ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | for x in option.ok_or("x not found") { = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loops_over_fallibles.rs:39:14 + --> $DIR/for_loops_over_fallibles.rs:40:14 | LL | for x in v.iter().next() { | ^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | for x in v.iter().next() { = note: `#[deny(clippy::iter_next_loop)]` on by default error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:44:14 + --> $DIR/for_loops_over_fallibles.rs:45:14 | LL | for x in v.iter().next().and(Some(0)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | for x in v.iter().next().and(Some(0)) { = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:48:14 + --> $DIR/for_loops_over_fallibles.rs:49:14 | LL | for x in v.iter().next().ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | for x in v.iter().next().ok_or("x not found") { = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:60:5 + --> $DIR/for_loops_over_fallibles.rs:61:5 | LL | / while let Some(x) = option { LL | | println!("{}", x); @@ -83,7 +83,7 @@ LL | | } = note: `#[deny(clippy::never_loop)]` on by default error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:66:5 + --> $DIR/for_loops_over_fallibles.rs:67:5 | LL | / while let Ok(x) = result { LL | | println!("{}", x); diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index e0c5f692740a..beedf2c1db29 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -1,13 +1,13 @@ // run-rustfix - +#![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args, - clippy::needless_borrow + clippy::needless_borrow, + clippy::uninlined_format_args )] -#![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/tests/ui/format.rs b/tests/ui/format.rs index ff83cd64bf09..e805f1818898 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -1,13 +1,13 @@ // run-rustfix - +#![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args, - clippy::needless_borrow + clippy::needless_borrow, + clippy::uninlined_format_args )] -#![warn(clippy::useless_format)] struct Foo(pub String); diff --git a/tests/ui/format_args.fixed b/tests/ui/format_args.fixed index e1c2d4d70be4..24cf0847dd58 100644 --- a/tests/ui/format_args.fixed +++ b/tests/ui/format_args.fixed @@ -1,10 +1,12 @@ // run-rustfix - -#![allow(unused)] -#![allow(clippy::assertions_on_constants)] -#![allow(clippy::eq_op)] -#![allow(clippy::print_literal)] #![warn(clippy::to_string_in_format_args)] +#![allow(unused)] +#![allow( + clippy::assertions_on_constants, + clippy::eq_op, + clippy::print_literal, + clippy::uninlined_format_args +)] use std::io::{stdout, Write}; use std::ops::Deref; diff --git a/tests/ui/format_args.rs b/tests/ui/format_args.rs index b9a4d66c28ad..753babf0afdc 100644 --- a/tests/ui/format_args.rs +++ b/tests/ui/format_args.rs @@ -1,10 +1,12 @@ // run-rustfix - -#![allow(unused)] -#![allow(clippy::assertions_on_constants)] -#![allow(clippy::eq_op)] -#![allow(clippy::print_literal)] #![warn(clippy::to_string_in_format_args)] +#![allow(unused)] +#![allow( + clippy::assertions_on_constants, + clippy::eq_op, + clippy::print_literal, + clippy::uninlined_format_args +)] use std::io::{stdout, Write}; use std::ops::Deref; diff --git a/tests/ui/format_args.stderr b/tests/ui/format_args.stderr index aa6e3659b43b..68b0bb9e089e 100644 --- a/tests/ui/format_args.stderr +++ b/tests/ui/format_args.stderr @@ -1,5 +1,5 @@ error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:74:72 + --> $DIR/format_args.rs:76:72 | LL | let _ = format!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this @@ -7,133 +7,133 @@ LL | let _ = format!("error: something failed at {}", Location::caller().to_ = note: `-D clippy::to-string-in-format-args` implied by `-D warnings` error: `to_string` applied to a type that implements `Display` in `write!` args - --> $DIR/format_args.rs:78:27 + --> $DIR/format_args.rs:80:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `writeln!` args - --> $DIR/format_args.rs:83:27 + --> $DIR/format_args.rs:85:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> $DIR/format_args.rs:85:63 + --> $DIR/format_args.rs:87:63 | LL | print!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:86:65 + --> $DIR/format_args.rs:88:65 | LL | println!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprint!` args - --> $DIR/format_args.rs:87:64 + --> $DIR/format_args.rs:89:64 | LL | eprint!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprintln!` args - --> $DIR/format_args.rs:88:66 + --> $DIR/format_args.rs:90:66 | LL | eprintln!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format_args!` args - --> $DIR/format_args.rs:89:77 + --> $DIR/format_args.rs:91:77 | LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert!` args - --> $DIR/format_args.rs:90:70 + --> $DIR/format_args.rs:92:70 | LL | assert!(true, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_eq!` args - --> $DIR/format_args.rs:91:73 + --> $DIR/format_args.rs:93:73 | LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_ne!` args - --> $DIR/format_args.rs:92:73 + --> $DIR/format_args.rs:94:73 | LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `panic!` args - --> $DIR/format_args.rs:93:63 + --> $DIR/format_args.rs:95:63 | LL | panic!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:94:20 + --> $DIR/format_args.rs:96:20 | LL | println!("{}", X(1).to_string()); | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:95:20 + --> $DIR/format_args.rs:97:20 | LL | println!("{}", Y(&X(1)).to_string()); | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:96:24 + --> $DIR/format_args.rs:98:24 | LL | println!("{}", Z(1).to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:97:20 + --> $DIR/format_args.rs:99:20 | LL | println!("{}", x.to_string()); | ^^^^^^^^^^^^^ help: use this: `**x` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:98:20 + --> $DIR/format_args.rs:100:20 | LL | println!("{}", x_ref.to_string()); | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:100:39 + --> $DIR/format_args.rs:102:39 | LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:101:52 + --> $DIR/format_args.rs:103:52 | LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:102:39 + --> $DIR/format_args.rs:104:39 | LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:103:52 + --> $DIR/format_args.rs:105:52 | LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:142:38 + --> $DIR/format_args.rs:144:38 | LL | let x = format!("{} {}", a, b.to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:156:24 + --> $DIR/format_args.rs:158:24 | LL | println!("{}", original[..10].to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]` diff --git a/tests/ui/format_args_unfixable.rs b/tests/ui/format_args_unfixable.rs index b24ddf7321e4..eb0ac15bfbf1 100644 --- a/tests/ui/format_args_unfixable.rs +++ b/tests/ui/format_args_unfixable.rs @@ -1,7 +1,5 @@ -#![allow(clippy::assertions_on_constants)] -#![allow(clippy::eq_op)] -#![warn(clippy::format_in_format_args)] -#![warn(clippy::to_string_in_format_args)] +#![warn(clippy::format_in_format_args, clippy::to_string_in_format_args)] +#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::uninlined_format_args)] use std::io::{stdout, Error, ErrorKind, Write}; use std::ops::Deref; diff --git a/tests/ui/format_args_unfixable.stderr b/tests/ui/format_args_unfixable.stderr index 37a6afb1ba7b..b291d475ad90 100644 --- a/tests/ui/format_args_unfixable.stderr +++ b/tests/ui/format_args_unfixable.stderr @@ -1,5 +1,5 @@ error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:27:5 + --> $DIR/format_args_unfixable.rs:25:5 | LL | println!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | println!("error: {}", format!("something failed at {}", Location::calle = note: `-D clippy::format-in-format-args` implied by `-D warnings` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:28:5 + --> $DIR/format_args_unfixable.rs:26:5 | LL | println!("{}: {}", error, format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -18,7 +18,7 @@ LL | println!("{}: {}", error, format!("something failed at {}", Location::c = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:29:5 + --> $DIR/format_args_unfixable.rs:27:5 | LL | println!("{:?}: {}", error, format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL | println!("{:?}: {}", error, format!("something failed at {}", Location: = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:30:5 + --> $DIR/format_args_unfixable.rs:28:5 | LL | println!("{{}}: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | println!("{{}}: {}", format!("something failed at {}", Location::caller = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:31:5 + --> $DIR/format_args_unfixable.rs:29:5 | LL | println!(r#"error: "{}""#, format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | println!(r#"error: "{}""#, format!("something failed at {}", Location:: = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:32:5 + --> $DIR/format_args_unfixable.rs:30:5 | LL | println!("error: {}", format!(r#"something failed at "{}""#, Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,7 +54,7 @@ LL | println!("error: {}", format!(r#"something failed at "{}""#, Location:: = help: or consider changing `format!` to `format_args!` error: `format!` in `println!` args - --> $DIR/format_args_unfixable.rs:33:5 + --> $DIR/format_args_unfixable.rs:31:5 | LL | println!("error: {}", format!("something failed at {} {0}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | println!("error: {}", format!("something failed at {} {0}", Location::c = help: or consider changing `format!` to `format_args!` error: `format!` in `format!` args - --> $DIR/format_args_unfixable.rs:34:13 + --> $DIR/format_args_unfixable.rs:32:13 | LL | let _ = format!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = format!("error: {}", format!("something failed at {}", Location = help: or consider changing `format!` to `format_args!` error: `format!` in `write!` args - --> $DIR/format_args_unfixable.rs:35:13 + --> $DIR/format_args_unfixable.rs:33:13 | LL | let _ = write!( | _____________^ @@ -86,7 +86,7 @@ LL | | ); = help: or consider changing `format!` to `format_args!` error: `format!` in `writeln!` args - --> $DIR/format_args_unfixable.rs:40:13 + --> $DIR/format_args_unfixable.rs:38:13 | LL | let _ = writeln!( | _____________^ @@ -100,7 +100,7 @@ LL | | ); = help: or consider changing `format!` to `format_args!` error: `format!` in `print!` args - --> $DIR/format_args_unfixable.rs:45:5 + --> $DIR/format_args_unfixable.rs:43:5 | LL | print!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | print!("error: {}", format!("something failed at {}", Location::caller( = help: or consider changing `format!` to `format_args!` error: `format!` in `eprint!` args - --> $DIR/format_args_unfixable.rs:46:5 + --> $DIR/format_args_unfixable.rs:44:5 | LL | eprint!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | eprint!("error: {}", format!("something failed at {}", Location::caller = help: or consider changing `format!` to `format_args!` error: `format!` in `eprintln!` args - --> $DIR/format_args_unfixable.rs:47:5 + --> $DIR/format_args_unfixable.rs:45:5 | LL | eprintln!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | eprintln!("error: {}", format!("something failed at {}", Location::call = help: or consider changing `format!` to `format_args!` error: `format!` in `format_args!` args - --> $DIR/format_args_unfixable.rs:48:13 + --> $DIR/format_args_unfixable.rs:46:13 | LL | let _ = format_args!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | let _ = format_args!("error: {}", format!("something failed at {}", Loc = help: or consider changing `format!` to `format_args!` error: `format!` in `assert!` args - --> $DIR/format_args_unfixable.rs:49:5 + --> $DIR/format_args_unfixable.rs:47:5 | LL | assert!(true, "error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | assert!(true, "error: {}", format!("something failed at {}", Location:: = help: or consider changing `format!` to `format_args!` error: `format!` in `assert_eq!` args - --> $DIR/format_args_unfixable.rs:50:5 + --> $DIR/format_args_unfixable.rs:48:5 | LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | assert_eq!(0, 0, "error: {}", format!("something failed at {}", Locatio = help: or consider changing `format!` to `format_args!` error: `format!` in `assert_ne!` args - --> $DIR/format_args_unfixable.rs:51:5 + --> $DIR/format_args_unfixable.rs:49:5 | LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -163,7 +163,7 @@ LL | assert_ne!(0, 0, "error: {}", format!("something failed at {}", Locatio = help: or consider changing `format!` to `format_args!` error: `format!` in `panic!` args - --> $DIR/format_args_unfixable.rs:52:5 + --> $DIR/format_args_unfixable.rs:50:5 | LL | panic!("error: {}", format!("something failed at {}", Location::caller())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/functions.rs b/tests/ui/functions.rs index 5521870eaecf..18149bfbc3fe 100644 --- a/tests/ui/functions.rs +++ b/tests/ui/functions.rs @@ -1,6 +1,6 @@ #![warn(clippy::all)] -#![allow(dead_code)] -#![allow(unused_unsafe, clippy::missing_safety_doc)] +#![allow(dead_code, unused_unsafe)] +#![allow(clippy::missing_safety_doc, clippy::uninlined_format_args)] // TOO_MANY_ARGUMENTS fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {} diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed index fa564e23cd27..e7b9a78c5dbc 100644 --- a/tests/ui/identity_op.fixed +++ b/tests/ui/identity_op.fixed @@ -1,13 +1,13 @@ // run-rustfix - #![warn(clippy::identity_op)] +#![allow(unused)] #![allow( clippy::eq_op, clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref, clippy::double_parens, - unused + clippy::uninlined_format_args )] use std::fmt::Write as _; diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 3d06d2a73b62..9a435cdbb753 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -1,13 +1,13 @@ // run-rustfix - #![warn(clippy::identity_op)] +#![allow(unused)] #![allow( clippy::eq_op, clippy::no_effect, clippy::unnecessary_operation, clippy::op_ref, clippy::double_parens, - unused + clippy::uninlined_format_args )] use std::fmt::Write as _; diff --git a/tests/ui/implicit_saturating_add.fixed b/tests/ui/implicit_saturating_add.fixed new file mode 100644 index 000000000000..7d363d59a6f0 --- /dev/null +++ b/tests/ui/implicit_saturating_add.fixed @@ -0,0 +1,106 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::implicit_saturating_add)] + +fn main() { + let mut u_8: u8 = 255; + let mut u_16: u16 = 500; + let mut u_32: u32 = 7000; + let mut u_64: u64 = 7000; + let mut i_8: i8 = 30; + let mut i_16: i16 = 500; + let mut i_32: i32 = 7000; + let mut i_64: i64 = 7000; + + if i_8 < 42 { + i_8 += 1; + } + if i_8 != 42 { + i_8 += 1; + } + + u_8 = u_8.saturating_add(1); + + u_8 = u_8.saturating_add(1); + + if u_8 < 15 { + u_8 += 1; + } + + u_16 = u_16.saturating_add(1); + + u_16 = u_16.saturating_add(1); + + u_16 = u_16.saturating_add(1); + + u_32 = u_32.saturating_add(1); + + u_32 = u_32.saturating_add(1); + + u_32 = u_32.saturating_add(1); + + u_64 = u_64.saturating_add(1); + + u_64 = u_64.saturating_add(1); + + u_64 = u_64.saturating_add(1); + + i_8 = i_8.saturating_add(1); + + i_8 = i_8.saturating_add(1); + + i_8 = i_8.saturating_add(1); + + i_16 = i_16.saturating_add(1); + + i_16 = i_16.saturating_add(1); + + i_16 = i_16.saturating_add(1); + + i_32 = i_32.saturating_add(1); + + i_32 = i_32.saturating_add(1); + + i_32 = i_32.saturating_add(1); + + i_64 = i_64.saturating_add(1); + + i_64 = i_64.saturating_add(1); + + i_64 = i_64.saturating_add(1); + + if i_64 < 42 { + i_64 += 1; + } + + if 42 > i_64 { + i_64 += 1; + } + + let a = 12; + let mut b = 8; + + if a < u8::MAX { + b += 1; + } + + if u8::MAX > a { + b += 1; + } + + if u_32 < u32::MAX { + u_32 += 1; + } else { + println!("don't lint this"); + } + + if u_32 < u32::MAX { + println!("don't lint this"); + u_32 += 1; + } + + if u_32 < 42 { + println!("brace yourself!"); + } else {u_32 = u_32.saturating_add(1); } +} diff --git a/tests/ui/implicit_saturating_add.rs b/tests/ui/implicit_saturating_add.rs new file mode 100644 index 000000000000..31a5916277fa --- /dev/null +++ b/tests/ui/implicit_saturating_add.rs @@ -0,0 +1,154 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::implicit_saturating_add)] + +fn main() { + let mut u_8: u8 = 255; + let mut u_16: u16 = 500; + let mut u_32: u32 = 7000; + let mut u_64: u64 = 7000; + let mut i_8: i8 = 30; + let mut i_16: i16 = 500; + let mut i_32: i32 = 7000; + let mut i_64: i64 = 7000; + + if i_8 < 42 { + i_8 += 1; + } + if i_8 != 42 { + i_8 += 1; + } + + if u_8 != u8::MAX { + u_8 += 1; + } + + if u_8 < u8::MAX { + u_8 += 1; + } + + if u_8 < 15 { + u_8 += 1; + } + + if u_16 != u16::MAX { + u_16 += 1; + } + + if u_16 < u16::MAX { + u_16 += 1; + } + + if u16::MAX > u_16 { + u_16 += 1; + } + + if u_32 != u32::MAX { + u_32 += 1; + } + + if u_32 < u32::MAX { + u_32 += 1; + } + + if u32::MAX > u_32 { + u_32 += 1; + } + + if u_64 != u64::MAX { + u_64 += 1; + } + + if u_64 < u64::MAX { + u_64 += 1; + } + + if u64::MAX > u_64 { + u_64 += 1; + } + + if i_8 != i8::MAX { + i_8 += 1; + } + + if i_8 < i8::MAX { + i_8 += 1; + } + + if i8::MAX > i_8 { + i_8 += 1; + } + + if i_16 != i16::MAX { + i_16 += 1; + } + + if i_16 < i16::MAX { + i_16 += 1; + } + + if i16::MAX > i_16 { + i_16 += 1; + } + + if i_32 != i32::MAX { + i_32 += 1; + } + + if i_32 < i32::MAX { + i_32 += 1; + } + + if i32::MAX > i_32 { + i_32 += 1; + } + + if i_64 != i64::MAX { + i_64 += 1; + } + + if i_64 < i64::MAX { + i_64 += 1; + } + + if i64::MAX > i_64 { + i_64 += 1; + } + + if i_64 < 42 { + i_64 += 1; + } + + if 42 > i_64 { + i_64 += 1; + } + + let a = 12; + let mut b = 8; + + if a < u8::MAX { + b += 1; + } + + if u8::MAX > a { + b += 1; + } + + if u_32 < u32::MAX { + u_32 += 1; + } else { + println!("don't lint this"); + } + + if u_32 < u32::MAX { + println!("don't lint this"); + u_32 += 1; + } + + if u_32 < 42 { + println!("brace yourself!"); + } else if u_32 < u32::MAX { + u_32 += 1; + } +} diff --git a/tests/ui/implicit_saturating_add.stderr b/tests/ui/implicit_saturating_add.stderr new file mode 100644 index 000000000000..42ae1b488853 --- /dev/null +++ b/tests/ui/implicit_saturating_add.stderr @@ -0,0 +1,197 @@ +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:23:5 + | +LL | / if u_8 != u8::MAX { +LL | | u_8 += 1; +LL | | } + | |_____^ help: use instead: `u_8 = u_8.saturating_add(1);` + | + = note: `-D clippy::implicit-saturating-add` implied by `-D warnings` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:27:5 + | +LL | / if u_8 < u8::MAX { +LL | | u_8 += 1; +LL | | } + | |_____^ help: use instead: `u_8 = u_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:35:5 + | +LL | / if u_16 != u16::MAX { +LL | | u_16 += 1; +LL | | } + | |_____^ help: use instead: `u_16 = u_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:39:5 + | +LL | / if u_16 < u16::MAX { +LL | | u_16 += 1; +LL | | } + | |_____^ help: use instead: `u_16 = u_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:43:5 + | +LL | / if u16::MAX > u_16 { +LL | | u_16 += 1; +LL | | } + | |_____^ help: use instead: `u_16 = u_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:47:5 + | +LL | / if u_32 != u32::MAX { +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `u_32 = u_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:51:5 + | +LL | / if u_32 < u32::MAX { +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `u_32 = u_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:55:5 + | +LL | / if u32::MAX > u_32 { +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `u_32 = u_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:59:5 + | +LL | / if u_64 != u64::MAX { +LL | | u_64 += 1; +LL | | } + | |_____^ help: use instead: `u_64 = u_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:63:5 + | +LL | / if u_64 < u64::MAX { +LL | | u_64 += 1; +LL | | } + | |_____^ help: use instead: `u_64 = u_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:67:5 + | +LL | / if u64::MAX > u_64 { +LL | | u_64 += 1; +LL | | } + | |_____^ help: use instead: `u_64 = u_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:71:5 + | +LL | / if i_8 != i8::MAX { +LL | | i_8 += 1; +LL | | } + | |_____^ help: use instead: `i_8 = i_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:75:5 + | +LL | / if i_8 < i8::MAX { +LL | | i_8 += 1; +LL | | } + | |_____^ help: use instead: `i_8 = i_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:79:5 + | +LL | / if i8::MAX > i_8 { +LL | | i_8 += 1; +LL | | } + | |_____^ help: use instead: `i_8 = i_8.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:83:5 + | +LL | / if i_16 != i16::MAX { +LL | | i_16 += 1; +LL | | } + | |_____^ help: use instead: `i_16 = i_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:87:5 + | +LL | / if i_16 < i16::MAX { +LL | | i_16 += 1; +LL | | } + | |_____^ help: use instead: `i_16 = i_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:91:5 + | +LL | / if i16::MAX > i_16 { +LL | | i_16 += 1; +LL | | } + | |_____^ help: use instead: `i_16 = i_16.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:95:5 + | +LL | / if i_32 != i32::MAX { +LL | | i_32 += 1; +LL | | } + | |_____^ help: use instead: `i_32 = i_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:99:5 + | +LL | / if i_32 < i32::MAX { +LL | | i_32 += 1; +LL | | } + | |_____^ help: use instead: `i_32 = i_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:103:5 + | +LL | / if i32::MAX > i_32 { +LL | | i_32 += 1; +LL | | } + | |_____^ help: use instead: `i_32 = i_32.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:107:5 + | +LL | / if i_64 != i64::MAX { +LL | | i_64 += 1; +LL | | } + | |_____^ help: use instead: `i_64 = i_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:111:5 + | +LL | / if i_64 < i64::MAX { +LL | | i_64 += 1; +LL | | } + | |_____^ help: use instead: `i_64 = i_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:115:5 + | +LL | / if i64::MAX > i_64 { +LL | | i_64 += 1; +LL | | } + | |_____^ help: use instead: `i_64 = i_64.saturating_add(1);` + +error: manual saturating add detected + --> $DIR/implicit_saturating_add.rs:151:12 + | +LL | } else if u_32 < u32::MAX { + | ____________^ +LL | | u_32 += 1; +LL | | } + | |_____^ help: use instead: `{u_32 = u_32.saturating_add(1); }` + +error: aborting due to 24 previous errors + diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.rs b/tests/ui/index_refutable_slice/if_let_slice_binding.rs index c2c0c520dc62..0a3374d11b03 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.rs +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.rs @@ -1,4 +1,5 @@ #![deny(clippy::index_refutable_slice)] +#![allow(clippy::uninlined_format_args)] enum SomeEnum { One(T), diff --git a/tests/ui/index_refutable_slice/if_let_slice_binding.stderr b/tests/ui/index_refutable_slice/if_let_slice_binding.stderr index a607df9b8766..0a13ac1354e5 100644 --- a/tests/ui/index_refutable_slice/if_let_slice_binding.stderr +++ b/tests/ui/index_refutable_slice/if_let_slice_binding.stderr @@ -1,5 +1,5 @@ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:13:17 + --> $DIR/if_let_slice_binding.rs:14:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -19,7 +19,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:19:17 + --> $DIR/if_let_slice_binding.rs:20:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -34,7 +34,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:25:17 + --> $DIR/if_let_slice_binding.rs:26:17 | LL | if let Some(slice) = slice { | ^^^^^ @@ -50,7 +50,7 @@ LL ~ println!("{}", slice_0); | error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:32:26 + --> $DIR/if_let_slice_binding.rs:33:26 | LL | if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped { | ^^^^^ @@ -65,7 +65,7 @@ LL | println!("{}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:39:29 + --> $DIR/if_let_slice_binding.rs:40:29 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ @@ -80,7 +80,7 @@ LL | println!("{} -> {}", a_2, b[1]); | ~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:39:38 + --> $DIR/if_let_slice_binding.rs:40:38 | LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) { | ^ @@ -95,7 +95,7 @@ LL | println!("{} -> {}", a[2], b_1); | ~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:46:21 + --> $DIR/if_let_slice_binding.rs:47:21 | LL | if let Some(ref slice) = slice { | ^^^^^ @@ -110,7 +110,7 @@ LL | println!("{:?}", slice_1); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:54:17 + --> $DIR/if_let_slice_binding.rs:55:17 | LL | if let Some(slice) = &slice { | ^^^^^ @@ -125,7 +125,7 @@ LL | println!("{:?}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:123:17 + --> $DIR/if_let_slice_binding.rs:124:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ @@ -140,7 +140,7 @@ LL | println!("This is awesome! {}", slice_0); | ~~~~~~~ error: this binding can be a slice pattern to avoid indexing - --> $DIR/if_let_slice_binding.rs:130:17 + --> $DIR/if_let_slice_binding.rs:131:17 | LL | if let Some(slice) = wrap.inner { | ^^^^^ diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index a1e5fad0c621..622644f675d3 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { x * x < 64 diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index ba277e36339a..b911163f715e 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -1,29 +1,29 @@ error: infinite iteration detected - --> $DIR/infinite_iter.rs:9:5 + --> $DIR/infinite_iter.rs:11:5 | LL | repeat(0_u8).collect::>(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/infinite_iter.rs:7:8 + --> $DIR/infinite_iter.rs:9:8 | LL | #[deny(clippy::infinite_iter)] | ^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:10:5 + --> $DIR/infinite_iter.rs:12:5 | LL | (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:11:5 + --> $DIR/infinite_iter.rs:13:5 | LL | (0..8_u64).chain(0..).max(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:16:5 + --> $DIR/infinite_iter.rs:18:5 | LL | / (0..8_u32) LL | | .rev() @@ -33,37 +33,37 @@ LL | | .for_each(|x| println!("{}", x)); // infinite iter | |________________________________________^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:22:5 + --> $DIR/infinite_iter.rs:24:5 | LL | (0_usize..).flat_map(|x| 0..x).product::(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:23:5 + --> $DIR/infinite_iter.rs:25:5 | LL | (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:30:5 + --> $DIR/infinite_iter.rs:32:5 | LL | (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/infinite_iter.rs:28:8 + --> $DIR/infinite_iter.rs:30:8 | LL | #[deny(clippy::maybe_infinite_iter)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:31:5 + --> $DIR/infinite_iter.rs:33:5 | LL | repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:32:5 + --> $DIR/infinite_iter.rs:34:5 | LL | / (1..) LL | | .scan(0, |state, x| { @@ -74,31 +74,31 @@ LL | | .min(); // maybe infinite iter | |______________^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:38:5 + --> $DIR/infinite_iter.rs:40:5 | LL | (0..).find(|x| *x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:39:5 + --> $DIR/infinite_iter.rs:41:5 | LL | (0..).position(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:40:5 + --> $DIR/infinite_iter.rs:42:5 | LL | (0..).any(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^ error: possible infinite iteration detected - --> $DIR/infinite_iter.rs:41:5 + --> $DIR/infinite_iter.rs:43:5 | LL | (0..).all(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:63:31 + --> $DIR/infinite_iter.rs:65:31 | LL | let _: HashSet = (0..).collect(); // Infinite iter | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/issue_2356.fixed b/tests/ui/issue_2356.fixed index 942e99fa8787..a73ee0fb2e59 100644 --- a/tests/ui/issue_2356.fixed +++ b/tests/ui/issue_2356.fixed @@ -1,6 +1,7 @@ // run-rustfix #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] +#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; diff --git a/tests/ui/issue_2356.rs b/tests/ui/issue_2356.rs index b000234ea596..9dd9069609b1 100644 --- a/tests/ui/issue_2356.rs +++ b/tests/ui/issue_2356.rs @@ -1,6 +1,7 @@ // run-rustfix #![deny(clippy::while_let_on_iterator)] #![allow(unused_mut)] +#![allow(clippy::uninlined_format_args)] use std::iter::Iterator; diff --git a/tests/ui/issue_2356.stderr b/tests/ui/issue_2356.stderr index 4e3ff7522e0b..a24b0b32e470 100644 --- a/tests/ui/issue_2356.stderr +++ b/tests/ui/issue_2356.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> $DIR/issue_2356.rs:17:9 + --> $DIR/issue_2356.rs:18:9 | LL | while let Some(e) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` diff --git a/tests/ui/issue_4266.rs b/tests/ui/issue_4266.rs index d9d48189bd74..8e0620e52b65 100644 --- a/tests/ui/issue_4266.rs +++ b/tests/ui/issue_4266.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] +#![allow(clippy::uninlined_format_args)] async fn sink1<'a>(_: &'a str) {} // lint async fn sink1_elided(_: &str) {} // ok diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index 240f4bcc38fd..fb2a93c9580e 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -1,5 +1,5 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/issue_4266.rs:3:1 + --> $DIR/issue_4266.rs:4:1 | LL | async fn sink1<'a>(_: &'a str) {} // lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | async fn sink1<'a>(_: &'a str) {} // lint = note: `-D clippy::needless-lifetimes` implied by `-D warnings` error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/issue_4266.rs:7:1 + --> $DIR/issue_4266.rs:8:1 | LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: methods called `new` usually take no `self` - --> $DIR/issue_4266.rs:27:22 + --> $DIR/issue_4266.rs:28:22 | LL | pub async fn new(&mut self) -> Self { | ^^^^^^^^^ diff --git a/tests/ui/item_after_statement.rs b/tests/ui/item_after_statement.rs index d439ca1e4e1a..5e92dcab1f5a 100644 --- a/tests/ui/item_after_statement.rs +++ b/tests/ui/item_after_statement.rs @@ -1,4 +1,5 @@ #![warn(clippy::items_after_statements)] +#![allow(clippy::uninlined_format_args)] fn ok() { fn foo() { diff --git a/tests/ui/item_after_statement.stderr b/tests/ui/item_after_statement.stderr index ab4a6374c73c..2523c53ac53a 100644 --- a/tests/ui/item_after_statement.stderr +++ b/tests/ui/item_after_statement.stderr @@ -1,5 +1,5 @@ error: adding items after statements is confusing, since items exist from the start of the scope - --> $DIR/item_after_statement.rs:12:5 + --> $DIR/item_after_statement.rs:13:5 | LL | / fn foo() { LL | | println!("foo"); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::items-after-statements` implied by `-D warnings` error: adding items after statements is confusing, since items exist from the start of the scope - --> $DIR/item_after_statement.rs:19:5 + --> $DIR/item_after_statement.rs:20:5 | LL | / fn foo() { LL | | println!("foo"); @@ -17,7 +17,7 @@ LL | | } | |_____^ error: adding items after statements is confusing, since items exist from the start of the scope - --> $DIR/item_after_statement.rs:32:13 + --> $DIR/item_after_statement.rs:33:13 | LL | / fn say_something() { LL | | println!("something"); diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index 65598f1eaccc..26e3b8f63e70 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -4,7 +4,8 @@ // run-rustfix #![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)] macro_rules! one { () => { @@ -50,3 +51,14 @@ fn main() { assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); } + +fn issue7730(a: u8) { + // Suggestion should preserve comment + // comment +/* this is a + multiline + comment */ +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); +} diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index a0f31afd6ebf..7718588fdf6f 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -1,68 +1,124 @@ error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:30:5 + --> $DIR/manual_assert.rs:31:5 | LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);` + | |_____^ | = note: `-D clippy::manual-assert` implied by `-D warnings` +help: try instead + | +LL | assert!(a.is_empty(), "qaqaq{:?}", a); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:33:5 + --> $DIR/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");` + | |_____^ + | +help: try instead + | +LL | assert!(a.is_empty(), "qwqwq"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:50:5 + --> $DIR/manual_assert.rs:51:5 | LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ help: try: `assert!(!b.is_empty(), "panic1");` + | |_____^ + | +help: try instead + | +LL | assert!(!b.is_empty(), "panic1"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:53:5 + --> $DIR/manual_assert.rs:54:5 | LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:56:5 + --> $DIR/manual_assert.rs:57:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:59:5 + --> $DIR/manual_assert.rs:60:5 | LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:62:5 + --> $DIR/manual_assert.rs:63:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:65:5 + --> $DIR/manual_assert.rs:66:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ help: try: `assert!(!a.is_empty(), "with expansion {}", one!());` + | |_____^ + | +help: try instead + | +LL | assert!(!a.is_empty(), "with expansion {}", one!()); + | + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:73:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` +LL | | } + | |_____^ + | +help: try instead + | +LL | assert!(!(a > 2), "panic with comment"); + | -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed index 65598f1eaccc..26e3b8f63e70 100644 --- a/tests/ui/manual_assert.edition2021.fixed +++ b/tests/ui/manual_assert.edition2021.fixed @@ -4,7 +4,8 @@ // run-rustfix #![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)] macro_rules! one { () => { @@ -50,3 +51,14 @@ fn main() { assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); } + +fn issue7730(a: u8) { + // Suggestion should preserve comment + // comment +/* this is a + multiline + comment */ +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); +} diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index a0f31afd6ebf..7718588fdf6f 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -1,68 +1,124 @@ error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:30:5 + --> $DIR/manual_assert.rs:31:5 | LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);` + | |_____^ | = note: `-D clippy::manual-assert` implied by `-D warnings` +help: try instead + | +LL | assert!(a.is_empty(), "qaqaq{:?}", a); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:33:5 + --> $DIR/manual_assert.rs:34:5 | LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ help: try: `assert!(a.is_empty(), "qwqwq");` + | |_____^ + | +help: try instead + | +LL | assert!(a.is_empty(), "qwqwq"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:50:5 + --> $DIR/manual_assert.rs:51:5 | LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ help: try: `assert!(!b.is_empty(), "panic1");` + | |_____^ + | +help: try instead + | +LL | assert!(!b.is_empty(), "panic1"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:53:5 + --> $DIR/manual_assert.rs:54:5 | LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:56:5 + --> $DIR/manual_assert.rs:57:5 | LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:59:5 + --> $DIR/manual_assert.rs:60:5 | LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + | |_____^ + | +help: try instead + | +LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:62:5 + --> $DIR/manual_assert.rs:63:5 | LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + | |_____^ + | +help: try instead + | +LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + | error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:65:5 + --> $DIR/manual_assert.rs:66:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ help: try: `assert!(!a.is_empty(), "with expansion {}", one!());` + | |_____^ + | +help: try instead + | +LL | assert!(!a.is_empty(), "with expansion {}", one!()); + | + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:73:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` +LL | | } + | |_____^ + | +help: try instead + | +LL | assert!(!(a > 2), "panic with comment"); + | -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_assert.fixed b/tests/ui/manual_assert.fixed deleted file mode 100644 index a2393674fe61..000000000000 --- a/tests/ui/manual_assert.fixed +++ /dev/null @@ -1,45 +0,0 @@ -// revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 -// run-rustfix - -#![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] - -fn main() { - let a = vec![1, 2, 3]; - let c = Some(2); - if !a.is_empty() - && a.len() == 3 - && c.is_some() - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - && !a.is_empty() - && a.len() == 3 - { - panic!("qaqaq{:?}", a); - } - assert!(a.is_empty(), "qaqaq{:?}", a); - assert!(a.is_empty(), "qwqwq"); - if a.len() == 3 { - println!("qwq"); - println!("qwq"); - println!("qwq"); - } - if let Some(b) = c { - panic!("orz {}", b); - } - if a.len() == 3 { - panic!("qaqaq"); - } else { - println!("qwq"); - } - let b = vec![1, 2, 3]; - assert!(!b.is_empty(), "panic1"); - assert!(!(b.is_empty() && a.is_empty()), "panic2"); - assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - assert!(!(b.is_empty() || a.is_empty()), "panic4"); - assert!(!(a.is_empty() || !b.is_empty()), "panic5"); -} diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index 4d2706dd6211..8c37753071df 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -4,7 +4,8 @@ // run-rustfix #![warn(clippy::manual_assert)] -#![allow(clippy::nonminimal_bool)] +#![allow(dead_code, unused_doc_comments)] +#![allow(clippy::nonminimal_bool, clippy::uninlined_format_args)] macro_rules! one { () => { @@ -66,3 +67,15 @@ fn main() { panic!("with expansion {}", one!()) } } + +fn issue7730(a: u8) { + // Suggestion should preserve comment + if a > 2 { + // comment + /* this is a + multiline + comment */ + /// Doc comment + panic!("panic with comment") // comment after `panic!` + } +} diff --git a/tests/ui/manual_bits.fixed b/tests/ui/manual_bits.fixed index 386360dbdcdb..e7f8cd878ca7 100644 --- a/tests/ui/manual_bits.fixed +++ b/tests/ui/manual_bits.fixed @@ -6,7 +6,8 @@ clippy::useless_conversion, path_statements, unused_must_use, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::unnecessary_cast )] use std::mem::{size_of, size_of_val}; diff --git a/tests/ui/manual_bits.rs b/tests/ui/manual_bits.rs index 62638f047eb0..7b1d15495287 100644 --- a/tests/ui/manual_bits.rs +++ b/tests/ui/manual_bits.rs @@ -6,7 +6,8 @@ clippy::useless_conversion, path_statements, unused_must_use, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::unnecessary_cast )] use std::mem::{size_of, size_of_val}; diff --git a/tests/ui/manual_bits.stderr b/tests/ui/manual_bits.stderr index 69c591a203d3..652fafbc41d8 100644 --- a/tests/ui/manual_bits.stderr +++ b/tests/ui/manual_bits.stderr @@ -1,5 +1,5 @@ error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:15:5 + --> $DIR/manual_bits.rs:16:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` @@ -7,169 +7,169 @@ LL | size_of::() * 8; = note: `-D clippy::manual-bits` implied by `-D warnings` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:16:5 + --> $DIR/manual_bits.rs:17:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:17:5 + --> $DIR/manual_bits.rs:18:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:18:5 + --> $DIR/manual_bits.rs:19:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:19:5 + --> $DIR/manual_bits.rs:20:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:20:5 + --> $DIR/manual_bits.rs:21:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:22:5 + --> $DIR/manual_bits.rs:23:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:23:5 + --> $DIR/manual_bits.rs:24:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:24:5 + --> $DIR/manual_bits.rs:25:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:25:5 + --> $DIR/manual_bits.rs:26:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:26:5 + --> $DIR/manual_bits.rs:27:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:27:5 + --> $DIR/manual_bits.rs:28:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:29:5 + --> $DIR/manual_bits.rs:30:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:30:5 + --> $DIR/manual_bits.rs:31:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:31:5 + --> $DIR/manual_bits.rs:32:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:32:5 + --> $DIR/manual_bits.rs:33:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:33:5 + --> $DIR/manual_bits.rs:34:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:34:5 + --> $DIR/manual_bits.rs:35:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:36:5 + --> $DIR/manual_bits.rs:37:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:37:5 + --> $DIR/manual_bits.rs:38:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:38:5 + --> $DIR/manual_bits.rs:39:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:39:5 + --> $DIR/manual_bits.rs:40:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:40:5 + --> $DIR/manual_bits.rs:41:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:41:5 + --> $DIR/manual_bits.rs:42:5 | LL | 8 * size_of::(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:51:5 + --> $DIR/manual_bits.rs:52:5 | LL | size_of::() * 8; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:55:18 + --> $DIR/manual_bits.rs:56:18 | LL | let _: u32 = (size_of::() * 8) as u32; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:56:18 + --> $DIR/manual_bits.rs:57:18 | LL | let _: u32 = (size_of::() * 8).try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:57:13 + --> $DIR/manual_bits.rs:58:13 | LL | let _ = (size_of::() * 8).pow(5); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:58:14 + --> $DIR/manual_bits.rs:59:14 | LL | let _ = &(size_of::() * 8); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs new file mode 100644 index 000000000000..54fd888af99f --- /dev/null +++ b/tests/ui/manual_clamp.rs @@ -0,0 +1,304 @@ +#![warn(clippy::manual_clamp)] +#![allow( + unused, + dead_code, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::if_same_then_else +)] + +use std::cmp::{max as cmp_max, min as cmp_min}; + +const CONST_MAX: i32 = 10; +const CONST_MIN: i32 = 4; + +const CONST_F64_MAX: f64 = 10.0; +const CONST_F64_MIN: f64 = 4.0; + +fn main() { + let (input, min, max) = (0, -2, 3); + // Lint + let x0 = if max < input { + max + } else if min > input { + min + } else { + input + }; + + let x1 = if input > max { + max + } else if input < min { + min + } else { + input + }; + + let x2 = if input < min { + min + } else if input > max { + max + } else { + input + }; + + let x3 = if min > input { + min + } else if max < input { + max + } else { + input + }; + + let x4 = input.max(min).min(max); + + let x5 = input.min(max).max(min); + + let x6 = match input { + x if x > max => max, + x if x < min => min, + x => x, + }; + + let x7 = match input { + x if x < min => min, + x if x > max => max, + x => x, + }; + + let x8 = match input { + x if max < x => max, + x if min > x => min, + x => x, + }; + + let mut x9 = input; + if x9 < min { + x9 = min; + } + if x9 > max { + x9 = max; + } + + let x10 = match input { + x if min > x => min, + x if max < x => max, + x => x, + }; + + let mut x11 = input; + let _ = 1; + if x11 > max { + x11 = max; + } + if x11 < min { + x11 = min; + } + + let mut x12 = input; + if min > x12 { + x12 = min; + } + if max < x12 { + x12 = max; + } + + let mut x13 = input; + if max < x13 { + x13 = max; + } + if min > x13 { + x13 = min; + } + + let x14 = if input > CONST_MAX { + CONST_MAX + } else if input < CONST_MIN { + CONST_MIN + } else { + input + }; + { + let (input, min, max) = (0.0f64, -2.0, 3.0); + let x15 = if input > max { + max + } else if input < min { + min + } else { + input + }; + } + { + let input: i32 = cmp_min_max(1); + // These can only be detected if exactly one of the arguments to the inner function is const. + let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); + let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); + let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); + let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); + let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); + let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); + let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); + let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); + let input: f64 = cmp_min_max(1) as f64; + let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); + let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); + let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); + let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); + let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); + let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); + let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); + let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); + } + let mut x32 = input; + if x32 < min { + x32 = min; + } else if x32 > max { + x32 = max; + } + + // It's important this be the last set of statements + let mut x33 = input; + if max < x33 { + x33 = max; + } + if min > x33 { + x33 = min; + } +} + +// This code intentionally nonsense. +fn no_lint() { + let (input, min, max) = (0, -2, 3); + let x0 = if max < input { + max + } else if min > input { + max + } else { + min + }; + + let x1 = if input > max { + max + } else if input > min { + min + } else { + max + }; + + let x2 = if max < min { + min + } else if input > max { + input + } else { + input + }; + + let x3 = if min > input { + input + } else if max < input { + max + } else { + max + }; + + let x6 = match input { + x if x < max => x, + x if x < min => x, + x => x, + }; + + let x7 = match input { + x if x < min => max, + x if x > max => min, + x => x, + }; + + let x8 = match input { + x if max > x => max, + x if min > x => min, + x => x, + }; + + let mut x9 = input; + if x9 > min { + x9 = min; + } + if x9 > max { + x9 = max; + } + + let x10 = match input { + x if min > x => min, + x if max < x => max, + x => min, + }; + + let mut x11 = input; + if x11 > max { + x11 = min; + } + if x11 < min { + x11 = max; + } + + let mut x12 = input; + if min > x12 { + x12 = max * 3; + } + if max < x12 { + x12 = min; + } + + let mut x13 = input; + if max < x13 { + let x13 = max; + } + if min > x13 { + x13 = min; + } + let mut x14 = input; + if x14 < min { + x14 = 3; + } else if x14 > max { + x14 = max; + } + { + let input: i32 = cmp_min_max(1); + // These can only be detected if exactly one of the arguments to the inner function is const. + let x16 = cmp_max(cmp_max(input, CONST_MAX), CONST_MIN); + let x17 = cmp_min(cmp_min(input, CONST_MIN), CONST_MAX); + let x18 = cmp_max(CONST_MIN, cmp_max(input, CONST_MAX)); + let x19 = cmp_min(CONST_MAX, cmp_min(input, CONST_MIN)); + let x20 = cmp_max(cmp_max(CONST_MAX, input), CONST_MIN); + let x21 = cmp_min(cmp_min(CONST_MIN, input), CONST_MAX); + let x22 = cmp_max(CONST_MIN, cmp_max(CONST_MAX, input)); + let x23 = cmp_min(CONST_MAX, cmp_min(CONST_MIN, input)); + let input: f64 = cmp_min_max(1) as f64; + let x24 = f64::max(f64::max(input, CONST_F64_MAX), CONST_F64_MIN); + let x25 = f64::min(f64::min(input, CONST_F64_MIN), CONST_F64_MAX); + let x26 = f64::max(CONST_F64_MIN, f64::max(input, CONST_F64_MAX)); + let x27 = f64::min(CONST_F64_MAX, f64::min(input, CONST_F64_MIN)); + let x28 = f64::max(f64::max(CONST_F64_MAX, input), CONST_F64_MIN); + let x29 = f64::min(f64::min(CONST_F64_MIN, input), CONST_F64_MAX); + let x30 = f64::max(CONST_F64_MIN, f64::max(CONST_F64_MAX, input)); + let x31 = f64::min(CONST_F64_MAX, f64::min(CONST_F64_MIN, input)); + let x32 = f64::min(CONST_F64_MAX, f64::min(CONST_F64_MIN, CONST_F64_MAX)); + } +} + +fn dont_tell_me_what_to_do() { + let (input, min, max) = (0, -2, 3); + let mut x_never = input; + #[allow(clippy::manual_clamp)] + if x_never < min { + x_never = min; + } + if x_never > max { + x_never = max; + } +} + +/// Just to ensure this isn't const evaled +fn cmp_min_max(input: i32) -> i32 { + input * 3 +} diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr new file mode 100644 index 000000000000..0604f8606c3f --- /dev/null +++ b/tests/ui/manual_clamp.stderr @@ -0,0 +1,375 @@ +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:76:5 + | +LL | / if x9 < min { +LL | | x9 = min; +LL | | } +LL | | if x9 > max { +LL | | x9 = max; +LL | | } + | |_____^ help: replace with clamp: `x9 = x9.clamp(min, max);` + | + = note: clamp will panic if max < min + = note: `-D clippy::manual-clamp` implied by `-D warnings` + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:91:5 + | +LL | / if x11 > max { +LL | | x11 = max; +LL | | } +LL | | if x11 < min { +LL | | x11 = min; +LL | | } + | |_____^ help: replace with clamp: `x11 = x11.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:99:5 + | +LL | / if min > x12 { +LL | | x12 = min; +LL | | } +LL | | if max < x12 { +LL | | x12 = max; +LL | | } + | |_____^ help: replace with clamp: `x12 = x12.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:107:5 + | +LL | / if max < x13 { +LL | | x13 = max; +LL | | } +LL | | if min > x13 { +LL | | x13 = min; +LL | | } + | |_____^ help: replace with clamp: `x13 = x13.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:161:5 + | +LL | / if max < x33 { +LL | | x33 = max; +LL | | } +LL | | if min > x33 { +LL | | x33 = min; +LL | | } + | |_____^ help: replace with clamp: `x33 = x33.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:21:14 + | +LL | let x0 = if max < input { + | ______________^ +LL | | max +LL | | } else if min > input { +LL | | min +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:29:14 + | +LL | let x1 = if input > max { + | ______________^ +LL | | max +LL | | } else if input < min { +LL | | min +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:37:14 + | +LL | let x2 = if input < min { + | ______________^ +LL | | min +LL | | } else if input > max { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:45:14 + | +LL | let x3 = if min > input { + | ______________^ +LL | | min +LL | | } else if max < input { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:53:14 + | +LL | let x4 = input.max(min).min(max); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:55:14 + | +LL | let x5 = input.min(max).max(min); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:57:14 + | +LL | let x6 = match input { + | ______________^ +LL | | x if x > max => max, +LL | | x if x < min => min, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:63:14 + | +LL | let x7 = match input { + | ______________^ +LL | | x if x < min => min, +LL | | x if x > max => max, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:69:14 + | +LL | let x8 = match input { + | ______________^ +LL | | x if max < x => max, +LL | | x if min > x => min, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:83:15 + | +LL | let x10 = match input { + | _______________^ +LL | | x if min > x => min, +LL | | x if max < x => max, +LL | | x => x, +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:114:15 + | +LL | let x14 = if input > CONST_MAX { + | _______________^ +LL | | CONST_MAX +LL | | } else if input < CONST_MIN { +LL | | CONST_MIN +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:123:19 + | +LL | let x15 = if input > max { + | ___________________^ +LL | | max +LL | | } else if input < min { +LL | | min +LL | | } else { +LL | | input +LL | | }; + | |_________^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:134:19 + | +LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:135:19 + | +LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:136:19 + | +LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:137:19 + | +LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:138:19 + | +LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:139:19 + | +LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:140:19 + | +LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:141:19 + | +LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` + | + = note: clamp will panic if max < min + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:143:19 + | +LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:144:19 + | +LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:145:19 + | +LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:146:19 + | +LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:147:19 + | +LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:148:19 + | +LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:149:19 + | +LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:150:19 + | +LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` + | + = note: clamp will panic if max < min, min.is_nan(), or max.is_nan() + = note: clamp returns NaN if the input is NaN + +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:153:5 + | +LL | / if x32 < min { +LL | | x32 = min; +LL | | } else if x32 > max { +LL | | x32 = max; +LL | | } + | |_____^ help: replace with clamp: `x32 = x32.clamp(min, max);` + | + = note: clamp will panic if max < min + +error: aborting due to 34 previous errors + diff --git a/tests/ui/manual_find_fixable.fixed b/tests/ui/manual_find_fixable.fixed index 36d1644c22bd..2bce6e624c90 100644 --- a/tests/ui/manual_find_fixable.fixed +++ b/tests/ui/manual_find_fixable.fixed @@ -1,7 +1,7 @@ // run-rustfix - -#![allow(unused, clippy::needless_return)] #![warn(clippy::manual_find)] +#![allow(unused)] +#![allow(clippy::needless_return, clippy::uninlined_format_args)] use std::collections::HashMap; diff --git a/tests/ui/manual_find_fixable.rs b/tests/ui/manual_find_fixable.rs index ed277ddaa722..f5c6de37a257 100644 --- a/tests/ui/manual_find_fixable.rs +++ b/tests/ui/manual_find_fixable.rs @@ -1,7 +1,7 @@ // run-rustfix - -#![allow(unused, clippy::needless_return)] #![warn(clippy::manual_find)] +#![allow(unused)] +#![allow(clippy::needless_return, clippy::uninlined_format_args)] use std::collections::HashMap; diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index d922593bc6f9..96cd87c0e19a 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -1,5 +1,5 @@ #![warn(clippy::manual_flatten)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::uninlined_format_args)] fn main() { // Test for loop over implicitly adjusted `Iterator` with `if let` expression diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 87e16f5d09bd..5429fb4e454e 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,6 @@ // aux-build:option_helpers.rs - #![warn(clippy::map_unwrap_or)] +#![allow(clippy::uninlined_format_args)] #[macro_use] extern crate option_helpers; diff --git a/tests/ui/match_ref_pats.fixed b/tests/ui/match_ref_pats.fixed index 1b6c2d924121..cf37fc6dc90a 100644 --- a/tests/ui/match_ref_pats.fixed +++ b/tests/ui/match_ref_pats.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_ref_pats)] -#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] +#![allow(dead_code, unused_variables)] +#![allow(clippy::enum_variant_names, clippy::equatable_if_let, clippy::uninlined_format_args)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.rs b/tests/ui/match_ref_pats.rs index 68dfac4e2e97..3220b97d1b51 100644 --- a/tests/ui/match_ref_pats.rs +++ b/tests/ui/match_ref_pats.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_ref_pats)] -#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] +#![allow(dead_code, unused_variables)] +#![allow(clippy::enum_variant_names, clippy::equatable_if_let, clippy::uninlined_format_args)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index 353f7399d9c2..7d9646c842ee 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -1,5 +1,5 @@ error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:8:9 + --> $DIR/match_ref_pats.rs:9:9 | LL | / match v { LL | | &Some(v) => println!("{:?}", v), @@ -16,7 +16,7 @@ LL ~ None => println!("none"), | error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_ref_pats.rs:25:5 + --> $DIR/match_ref_pats.rs:26:5 | LL | / match &w { LL | | &Some(v) => println!("{:?}", v), @@ -32,7 +32,7 @@ LL ~ None => println!("none"), | error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:37:12 + --> $DIR/match_ref_pats.rs:38:12 | LL | if let &None = a { | -------^^^^^---- help: try this: `if a.is_none()` @@ -40,13 +40,13 @@ LL | if let &None = a { = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:42:12 + --> $DIR/match_ref_pats.rs:43:12 | LL | if let &None = &b { | -------^^^^^----- help: try this: `if b.is_none()` error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:102:9 + --> $DIR/match_ref_pats.rs:103:9 | LL | / match foobar_variant!(0) { LL | | &FooBar::Foo => println!("Foo"), diff --git a/tests/ui/match_result_ok.fixed b/tests/ui/match_result_ok.fixed index d4760a97567e..8b91b9854a04 100644 --- a/tests/ui/match_result_ok.fixed +++ b/tests/ui/match_result_ok.fixed @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::match_result_ok)] -#![allow(clippy::boxed_local)] #![allow(dead_code)] +#![allow(clippy::boxed_local, clippy::uninlined_format_args)] // Checking `if` cases diff --git a/tests/ui/match_result_ok.rs b/tests/ui/match_result_ok.rs index 0b818723d989..bc2c4b50e272 100644 --- a/tests/ui/match_result_ok.rs +++ b/tests/ui/match_result_ok.rs @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::match_result_ok)] -#![allow(clippy::boxed_local)] #![allow(dead_code)] +#![allow(clippy::boxed_local, clippy::uninlined_format_args)] // Checking `if` cases diff --git a/tests/ui/match_result_ok.stderr b/tests/ui/match_result_ok.stderr index cc3bc8c76ff3..98a95705ca59 100644 --- a/tests/ui/match_result_ok.stderr +++ b/tests/ui/match_result_ok.stderr @@ -1,5 +1,5 @@ error: matching on `Some` with `ok()` is redundant - --> $DIR/match_result_ok.rs:10:5 + --> $DIR/match_result_ok.rs:9:5 | LL | if let Some(y) = x.parse().ok() { y } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let Ok(y) = x.parse() { y } else { 0 } | ~~~~~~~~~~~~~~~~~~~~~~~~ error: matching on `Some` with `ok()` is redundant - --> $DIR/match_result_ok.rs:20:9 + --> $DIR/match_result_ok.rs:19:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | if let Ok(y) = x . parse() { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: matching on `Some` with `ok()` is redundant - --> $DIR/match_result_ok.rs:46:5 + --> $DIR/match_result_ok.rs:45:5 | LL | while let Some(a) = wat.next().ok() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index 61793e80c98d..82b2c433d99e 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -1,5 +1,9 @@ #![warn(clippy::match_same_arms)] -#![allow(clippy::disallowed_names, clippy::diverging_sub_expression)] +#![allow( + clippy::disallowed_names, + clippy::diverging_sub_expression, + clippy::uninlined_format_args +)] fn bar(_: T) {} fn foo() -> bool { diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index b260155d2189..06cd4300054d 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -1,5 +1,5 @@ error: this match arm has an identical body to the `_` wildcard arm - --> $DIR/match_same_arms2.rs:11:9 + --> $DIR/match_same_arms2.rs:15:9 | LL | / 42 => { LL | | foo(); @@ -12,7 +12,7 @@ LL | | }, | = help: or try changing either arm body note: `_` wildcard arm here - --> $DIR/match_same_arms2.rs:20:9 + --> $DIR/match_same_arms2.rs:24:9 | LL | / _ => { LL | | //~ ERROR match arms have same body @@ -25,7 +25,7 @@ LL | | }, = note: `-D clippy::match-same-arms` implied by `-D warnings` error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:34:9 + --> $DIR/match_same_arms2.rs:38:9 | LL | 51 => foo(), //~ ERROR match arms have same body | --^^^^^^^^^ @@ -34,13 +34,13 @@ LL | 51 => foo(), //~ ERROR match arms have same body | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:33:9 + --> $DIR/match_same_arms2.rs:37:9 | LL | 42 => foo(), | ^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:40:9 + --> $DIR/match_same_arms2.rs:44:9 | LL | None => 24, //~ ERROR match arms have same body | ----^^^^^^ @@ -49,13 +49,13 @@ LL | None => 24, //~ ERROR match arms have same body | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:39:9 + --> $DIR/match_same_arms2.rs:43:9 | LL | Some(_) => 24, | ^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:62:9 + --> $DIR/match_same_arms2.rs:66:9 | LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body | ---------------^^^^^^^^^^ @@ -64,13 +64,13 @@ LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:61:9 + --> $DIR/match_same_arms2.rs:65:9 | LL | (Some(a), None) => bar(a), | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:67:9 + --> $DIR/match_same_arms2.rs:71:9 | LL | (Some(a), ..) => bar(a), | -------------^^^^^^^^^^ @@ -79,13 +79,13 @@ LL | (Some(a), ..) => bar(a), | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:68:9 + --> $DIR/match_same_arms2.rs:72:9 | LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body | ^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:101:9 + --> $DIR/match_same_arms2.rs:105:9 | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ----------------^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,13 +94,13 @@ LL | (Ok(x), Some(_)) => println!("ok {}", x), | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:102:9 + --> $DIR/match_same_arms2.rs:106:9 | LL | (Ok(_), Some(x)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:117:9 + --> $DIR/match_same_arms2.rs:121:9 | LL | Ok(_) => println!("ok"), | -----^^^^^^^^^^^^^^^^^^ @@ -109,13 +109,13 @@ LL | Ok(_) => println!("ok"), | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:116:9 + --> $DIR/match_same_arms2.rs:120:9 | LL | Ok(3) => println!("ok"), | ^^^^^^^^^^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:144:9 + --> $DIR/match_same_arms2.rs:148:9 | LL | 1 => { | ^ help: try merging the arm patterns: `1 | 0` @@ -127,7 +127,7 @@ LL | | }, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:141:9 + --> $DIR/match_same_arms2.rs:145:9 | LL | / 0 => { LL | | empty!(0); @@ -135,7 +135,7 @@ LL | | }, | |_________^ error: match expression looks like `matches!` macro - --> $DIR/match_same_arms2.rs:162:16 + --> $DIR/match_same_arms2.rs:166:16 | LL | let _ans = match x { | ________________^ @@ -148,7 +148,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:194:9 + --> $DIR/match_same_arms2.rs:198:9 | LL | Foo::X(0) => 1, | ---------^^^^^ @@ -157,13 +157,13 @@ LL | Foo::X(0) => 1, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:196:9 + --> $DIR/match_same_arms2.rs:200:9 | LL | Foo::Z(_) => 1, | ^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:204:9 + --> $DIR/match_same_arms2.rs:208:9 | LL | Foo::Z(_) => 1, | ---------^^^^^ @@ -172,13 +172,13 @@ LL | Foo::Z(_) => 1, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:202:9 + --> $DIR/match_same_arms2.rs:206:9 | LL | Foo::X(0) => 1, | ^^^^^^^^^^^^^^ error: this match arm has an identical body to another arm - --> $DIR/match_same_arms2.rs:227:9 + --> $DIR/match_same_arms2.rs:231:9 | LL | Some(Bar { y: 0, x: 5, .. }) => 1, | ----------------------------^^^^^ @@ -187,7 +187,7 @@ LL | Some(Bar { y: 0, x: 5, .. }) => 1, | = help: or try changing either arm body note: other arm here - --> $DIR/match_same_arms2.rs:224:9 + --> $DIR/match_same_arms2.rs:228:9 | LL | Some(Bar { x: 0, y: 5, .. }) => 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index de46e6cff55b..951f552eb32b 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] -#![allow(unused_variables, clippy::toplevel_ref_arg)] +#![allow(unused_variables)] +#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)] struct Point { x: i32, diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index eea64fcb292b..19c0fee8fd68 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] -#![allow(unused_variables, clippy::toplevel_ref_arg)] +#![allow(unused_variables)] +#![allow(clippy::toplevel_ref_arg, clippy::uninlined_format_args)] struct Point { x: i32, diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index a91fcc2125d4..6a7db67e311a 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] #![allow(unused_variables)] +#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs index 476386ebabe2..5a4bb8441fff 100644 --- a/tests/ui/match_single_binding2.rs +++ b/tests/ui/match_single_binding2.rs @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::match_single_binding)] #![allow(unused_variables)] +#![allow(clippy::uninlined_format_args)] fn main() { // Lint (additional curly braces needed, see #6572) diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index b2bc97f4744a..24e52afd6917 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -1,4 +1,5 @@ #![warn(clippy::all)] +#![allow(clippy::manual_clamp)] use std::cmp::max as my_max; use std::cmp::min as my_min; diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index c70b77eabbd8..069d9068657e 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -1,5 +1,5 @@ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:23:5 + --> $DIR/min_max.rs:24:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ @@ -7,73 +7,73 @@ LL | min(1, max(3, x)); = note: `-D clippy::min-max` implied by `-D warnings` error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:24:5 + --> $DIR/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:25:5 + --> $DIR/min_max.rs:26:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:26:5 + --> $DIR/min_max.rs:27:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:28:5 + --> $DIR/min_max.rs:29:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:38:5 + --> $DIR/min_max.rs:39:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:39:5 + --> $DIR/min_max.rs:40:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:44:5 + --> $DIR/min_max.rs:45:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:45:5 + --> $DIR/min_max.rs:46:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:46:5 + --> $DIR/min_max.rs:47:5 | LL | f.max(3f32).min(1f32); | ^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:52:5 + --> $DIR/min_max.rs:53:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:55:5 + --> $DIR/min_max.rs:56:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:56:5 + --> $DIR/min_max.rs:57:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 44e407bd1ab2..c4c6391bb4c1 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -160,6 +160,17 @@ fn manual_rem_euclid() { let _: i32 = ((x % 4) + 4) % 4; } +fn manual_clamp() { + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} + fn main() { filter_map_next(); checked_conversion(); @@ -180,6 +191,7 @@ fn main() { err_expect(); cast_abs_to_unsigned(); manual_rem_euclid(); + manual_clamp(); } mod just_under_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 6e749d2741c4..d1cffc26a831 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:204:24 + --> $DIR/min_rust_version_attr.rs:216:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:203:9 + --> $DIR/min_rust_version_attr.rs:215:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL ~ assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:216:24 + --> $DIR/min_rust_version_attr.rs:228:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:215:9 + --> $DIR/min_rust_version_attr.rs:227:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index be854d941833..ac8fd9d8fb09 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -1,7 +1,7 @@ // aux-build:macro_rules.rs - -#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::mut_mut)] +#![allow(unused)] +#![allow(clippy::no_effect, clippy::uninlined_format_args, clippy::unnecessary_operation)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 8cf93bd24817..aa2687159ef4 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,9 +1,9 @@ // run-rustfix - #![feature(custom_inner_attributes, lint_reasons)] #[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables, clippy::unnecessary_mut_passed)] +#[allow(unused_variables)] +#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] fn main() { let a = 5; let ref_a = &a; @@ -298,3 +298,32 @@ mod meets_msrv { let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); } } + +#[allow(unused)] +fn issue9383() { + // Should not lint because unions need explicit deref when accessing field + use std::mem::ManuallyDrop; + + union Coral { + crab: ManuallyDrop>, + } + + union Ocean { + coral: ManuallyDrop, + } + + let mut ocean = Ocean { + coral: ManuallyDrop::new(Coral { + crab: ManuallyDrop::new(vec![1, 2, 3]), + }), + }; + + unsafe { + ManuallyDrop::drop(&mut (&mut ocean.coral).crab); + + (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); + ManuallyDrop::drop(&mut (*ocean.coral).crab); + + ManuallyDrop::drop(&mut ocean.coral); + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index fd9b2a11df96..d41251e8f6aa 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,9 +1,9 @@ // run-rustfix - #![feature(custom_inner_attributes, lint_reasons)] #[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables, clippy::unnecessary_mut_passed)] +#[allow(unused_variables)] +#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] fn main() { let a = 5; let ref_a = &a; @@ -298,3 +298,32 @@ mod meets_msrv { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } + +#[allow(unused)] +fn issue9383() { + // Should not lint because unions need explicit deref when accessing field + use std::mem::ManuallyDrop; + + union Coral { + crab: ManuallyDrop>, + } + + union Ocean { + coral: ManuallyDrop, + } + + let mut ocean = Ocean { + coral: ManuallyDrop::new(Coral { + crab: ManuallyDrop::new(vec![1, 2, 3]), + }), + }; + + unsafe { + ManuallyDrop::drop(&mut (&mut ocean.coral).crab); + + (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]); + ManuallyDrop::drop(&mut (*ocean.coral).crab); + + ManuallyDrop::drop(&mut ocean.coral); + } +} diff --git a/tests/ui/needless_borrowed_ref.fixed b/tests/ui/needless_borrowed_ref.fixed index a0937a2c5f62..bcb4eb2dd48a 100644 --- a/tests/ui/needless_borrowed_ref.fixed +++ b/tests/ui/needless_borrowed_ref.fixed @@ -1,17 +1,38 @@ // run-rustfix -#[warn(clippy::needless_borrowed_reference)] -#[allow(unused_variables)] -fn main() { +#![warn(clippy::needless_borrowed_reference)] +#![allow(unused, clippy::needless_borrow)] + +fn main() {} + +fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|a| a.is_empty()); - // ^ should be linted let var = 3; let thingy = Some(&var); - if let Some(&ref v) = thingy { - // ^ should be linted - } + if let Some(v) = thingy {} + + if let &[a, ref b] = slice_of_refs {} + + let [a, ..] = &array; + let [a, b, ..] = &array; + + if let [a, b] = slice {} + if let [a, b] = &vec[..] {} + + if let [a, b, ..] = slice {} + if let [a, .., b] = slice {} + if let [.., a, b] = slice {} +} + +fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { + if let [ref a] = slice {} + if let &[ref a, b] = slice {} + if let &[ref a, .., b] = slice {} + + // must not be removed as variables must be bound consistently across | patterns + if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} let mut var2 = 5; let thingy2 = Some(&mut var2); @@ -28,17 +49,15 @@ fn main() { } } -#[allow(dead_code)] enum Animal { Cat(u64), Dog(u64), } -#[allow(unused_variables)] -#[allow(dead_code)] fn foo(a: &Animal, b: &Animal) { match (a, b) { - (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted } diff --git a/tests/ui/needless_borrowed_ref.rs b/tests/ui/needless_borrowed_ref.rs index 500ac448f0d5..f6de1a6d83d1 100644 --- a/tests/ui/needless_borrowed_ref.rs +++ b/tests/ui/needless_borrowed_ref.rs @@ -1,17 +1,38 @@ // run-rustfix -#[warn(clippy::needless_borrowed_reference)] -#[allow(unused_variables)] -fn main() { +#![warn(clippy::needless_borrowed_reference)] +#![allow(unused, clippy::needless_borrow)] + +fn main() {} + +fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|&ref a| a.is_empty()); - // ^ should be linted let var = 3; let thingy = Some(&var); - if let Some(&ref v) = thingy { - // ^ should be linted - } + if let Some(&ref v) = thingy {} + + if let &[&ref a, ref b] = slice_of_refs {} + + let &[ref a, ..] = &array; + let &[ref a, ref b, ..] = &array; + + if let &[ref a, ref b] = slice {} + if let &[ref a, ref b] = &vec[..] {} + + if let &[ref a, ref b, ..] = slice {} + if let &[ref a, .., ref b] = slice {} + if let &[.., ref a, ref b] = slice {} +} + +fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { + if let [ref a] = slice {} + if let &[ref a, b] = slice {} + if let &[ref a, .., b] = slice {} + + // must not be removed as variables must be bound consistently across | patterns + if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} let mut var2 = 5; let thingy2 = Some(&mut var2); @@ -28,17 +49,15 @@ fn main() { } } -#[allow(dead_code)] enum Animal { Cat(u64), Dog(u64), } -#[allow(unused_variables)] -#[allow(dead_code)] fn foo(a: &Animal, b: &Animal) { match (a, b) { - (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted } diff --git a/tests/ui/needless_borrowed_ref.stderr b/tests/ui/needless_borrowed_ref.stderr index 0a5cfb3db0b1..7453542e673f 100644 --- a/tests/ui/needless_borrowed_ref.stderr +++ b/tests/ui/needless_borrowed_ref.stderr @@ -1,10 +1,123 @@ -error: this pattern takes a reference on something that is being de-referenced - --> $DIR/needless_borrowed_ref.rs:7:34 +error: this pattern takes a reference on something that is being dereferenced + --> $DIR/needless_borrowed_ref.rs:10:34 | LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); - | ^^^^^^ help: try removing the `&ref` part and just keep: `a` + | ^^^^^^ | = note: `-D clippy::needless-borrowed-reference` implied by `-D warnings` +help: try removing the `&ref` part + | +LL - let _ = v.iter_mut().filter(|&ref a| a.is_empty()); +LL + let _ = v.iter_mut().filter(|a| a.is_empty()); + | + +error: this pattern takes a reference on something that is being dereferenced + --> $DIR/needless_borrowed_ref.rs:14:17 + | +LL | if let Some(&ref v) = thingy {} + | ^^^^^^ + | +help: try removing the `&ref` part + | +LL - if let Some(&ref v) = thingy {} +LL + if let Some(v) = thingy {} + | + +error: this pattern takes a reference on something that is being dereferenced + --> $DIR/needless_borrowed_ref.rs:16:14 + | +LL | if let &[&ref a, ref b] = slice_of_refs {} + | ^^^^^^ + | +help: try removing the `&ref` part + | +LL - if let &[&ref a, ref b] = slice_of_refs {} +LL + if let &[a, ref b] = slice_of_refs {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:18:9 + | +LL | let &[ref a, ..] = &array; + | ^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - let &[ref a, ..] = &array; +LL + let [a, ..] = &array; + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:19:9 + | +LL | let &[ref a, ref b, ..] = &array; + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - let &[ref a, ref b, ..] = &array; +LL + let [a, b, ..] = &array; + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:21:12 + | +LL | if let &[ref a, ref b] = slice {} + | ^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, ref b] = slice {} +LL + if let [a, b] = slice {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:22:12 + | +LL | if let &[ref a, ref b] = &vec[..] {} + | ^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, ref b] = &vec[..] {} +LL + if let [a, b] = &vec[..] {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:24:12 + | +LL | if let &[ref a, ref b, ..] = slice {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, ref b, ..] = slice {} +LL + if let [a, b, ..] = slice {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:25:12 + | +LL | if let &[ref a, .., ref b] = slice {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, .., ref b] = slice {} +LL + if let [a, .., b] = slice {} + | + +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:26:12 + | +LL | if let &[.., ref a, ref b] = slice {} + | ^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[.., ref a, ref b] = slice {} +LL + if let [.., a, b] = slice {} + | -error: aborting due to previous error +error: aborting due to 10 previous errors diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 12a9ace1ee68..6d213b46c20c 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,3 +1,5 @@ +#![allow(clippy::uninlined_format_args)] + use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; fn main() { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 9f0880cc6069..99e1b91d8fea 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:5:39 + --> $DIR/needless_collect_indirect.rs:7:39 | LL | let indirect_iter = sample.iter().collect::>(); | ^^^^^^^ @@ -14,7 +14,7 @@ LL ~ sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:7:38 + --> $DIR/needless_collect_indirect.rs:9:38 | LL | let indirect_len = sample.iter().collect::>(); | ^^^^^^^ @@ -28,7 +28,7 @@ LL ~ sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:40 + --> $DIR/needless_collect_indirect.rs:11:40 | LL | let indirect_empty = sample.iter().collect::>(); | ^^^^^^^ @@ -42,7 +42,7 @@ LL ~ sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:11:43 + --> $DIR/needless_collect_indirect.rs:13:43 | LL | let indirect_contains = sample.iter().collect::>(); | ^^^^^^^ @@ -56,7 +56,7 @@ LL ~ sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:23:48 + --> $DIR/needless_collect_indirect.rs:25:48 | LL | let non_copy_contains = sample.into_iter().collect::>(); | ^^^^^^^ @@ -70,7 +70,7 @@ LL ~ sample.into_iter().any(|x| x == a); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:52:51 + --> $DIR/needless_collect_indirect.rs:54:51 | LL | let buffer: Vec<&str> = string.split('/').collect(); | ^^^^^^^ @@ -84,7 +84,7 @@ LL ~ string.split('/').count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:57:55 + --> $DIR/needless_collect_indirect.rs:59:55 | LL | let indirect_len: VecDeque<_> = sample.iter().collect(); | ^^^^^^^ @@ -98,7 +98,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:62:57 + --> $DIR/needless_collect_indirect.rs:64:57 | LL | let indirect_len: LinkedList<_> = sample.iter().collect(); | ^^^^^^^ @@ -112,7 +112,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:67:57 + --> $DIR/needless_collect_indirect.rs:69:57 | LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); | ^^^^^^^ @@ -126,7 +126,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:127:59 + --> $DIR/needless_collect_indirect.rs:129:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -143,7 +143,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == i); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:152:59 + --> $DIR/needless_collect_indirect.rs:154:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -160,7 +160,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:181:63 + --> $DIR/needless_collect_indirect.rs:183:63 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -177,7 +177,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:217:59 + --> $DIR/needless_collect_indirect.rs:219:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -195,7 +195,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:242:26 + --> $DIR/needless_collect_indirect.rs:244:26 | LL | let w = v.iter().collect::>(); | ^^^^^^^ @@ -211,7 +211,7 @@ LL ~ for _ in 0..v.iter().count() { | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:264:30 + --> $DIR/needless_collect_indirect.rs:266:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ @@ -227,7 +227,7 @@ LL ~ while 1 == v.iter().count() { | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:286:30 + --> $DIR/needless_collect_indirect.rs:288:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ diff --git a/tests/ui/needless_continue.rs b/tests/ui/needless_continue.rs index f105d3d659ac..c891c9de3aec 100644 --- a/tests/ui/needless_continue.rs +++ b/tests/ui/needless_continue.rs @@ -1,4 +1,5 @@ #![warn(clippy::needless_continue)] +#![allow(clippy::uninlined_format_args)] macro_rules! zero { ($x:expr) => { diff --git a/tests/ui/needless_continue.stderr b/tests/ui/needless_continue.stderr index 005ba010f34f..d99989b54fc2 100644 --- a/tests/ui/needless_continue.stderr +++ b/tests/ui/needless_continue.stderr @@ -1,5 +1,5 @@ error: this `else` block is redundant - --> $DIR/needless_continue.rs:29:16 + --> $DIR/needless_continue.rs:30:16 | LL | } else { | ________________^ @@ -35,7 +35,7 @@ LL | | } = note: `-D clippy::needless-continue` implied by `-D warnings` error: there is no need for an explicit `else` block for this `if` expression - --> $DIR/needless_continue.rs:44:9 + --> $DIR/needless_continue.rs:45:9 | LL | / if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { LL | | continue; @@ -55,7 +55,7 @@ LL | | } } error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:57:9 + --> $DIR/needless_continue.rs:58:9 | LL | continue; // should lint here | ^^^^^^^^^ @@ -63,7 +63,7 @@ LL | continue; // should lint here = help: consider dropping the `continue` expression error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:64:9 + --> $DIR/needless_continue.rs:65:9 | LL | continue; // should lint here | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | continue; // should lint here = help: consider dropping the `continue` expression error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:71:9 + --> $DIR/needless_continue.rs:72:9 | LL | continue // should lint here | ^^^^^^^^ @@ -79,7 +79,7 @@ LL | continue // should lint here = help: consider dropping the `continue` expression error: this `continue` expression is redundant - --> $DIR/needless_continue.rs:79:9 + --> $DIR/needless_continue.rs:80:9 | LL | continue // should lint here | ^^^^^^^^ @@ -87,7 +87,7 @@ LL | continue // should lint here = help: consider dropping the `continue` expression error: this `else` block is redundant - --> $DIR/needless_continue.rs:129:24 + --> $DIR/needless_continue.rs:130:24 | LL | } else { | ________________________^ @@ -110,7 +110,7 @@ LL | | } } error: there is no need for an explicit `else` block for this `if` expression - --> $DIR/needless_continue.rs:135:17 + --> $DIR/needless_continue.rs:136:17 | LL | / if condition() { LL | | continue; // should lint here diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index c1685f7b6d7a..09e671b88e1a 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -1,10 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] +#![allow(unused)] #![allow( - unused, - clippy::needless_return, + clippy::let_unit_value, clippy::match_single_binding, - clippy::let_unit_value + clippy::needless_return, + clippy::uninlined_format_args )] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index ad17b0956fa9..abb4045b9197 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -1,10 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] +#![allow(unused)] #![allow( - unused, - clippy::needless_return, + clippy::let_unit_value, clippy::match_single_binding, - clippy::let_unit_value + clippy::needless_return, + clippy::uninlined_format_args )] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.stderr b/tests/ui/needless_for_each_fixable.stderr index 08e995851d7a..aebb762cc228 100644 --- a/tests/ui/needless_for_each_fixable.stderr +++ b/tests/ui/needless_for_each_fixable.stderr @@ -1,5 +1,5 @@ error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:15:5 + --> $DIR/needless_for_each_fixable.rs:16:5 | LL | / v.iter().for_each(|elem| { LL | | acc += elem; @@ -15,7 +15,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:18:5 + --> $DIR/needless_for_each_fixable.rs:19:5 | LL | / v.into_iter().for_each(|elem| { LL | | acc += elem; @@ -30,7 +30,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:22:5 + --> $DIR/needless_for_each_fixable.rs:23:5 | LL | / [1, 2, 3].iter().for_each(|elem| { LL | | acc += elem; @@ -45,7 +45,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:27:5 + --> $DIR/needless_for_each_fixable.rs:28:5 | LL | / hash_map.iter().for_each(|(k, v)| { LL | | acc += k + v; @@ -60,7 +60,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:30:5 + --> $DIR/needless_for_each_fixable.rs:31:5 | LL | / hash_map.iter_mut().for_each(|(k, v)| { LL | | acc += *k + *v; @@ -75,7 +75,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:33:5 + --> $DIR/needless_for_each_fixable.rs:34:5 | LL | / hash_map.keys().for_each(|k| { LL | | acc += k; @@ -90,7 +90,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:36:5 + --> $DIR/needless_for_each_fixable.rs:37:5 | LL | / hash_map.values().for_each(|v| { LL | | acc += v; @@ -105,7 +105,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:43:5 + --> $DIR/needless_for_each_fixable.rs:44:5 | LL | / my_vec().iter().for_each(|elem| { LL | | acc += elem; diff --git a/tests/ui/needless_for_each_unfixable.rs b/tests/ui/needless_for_each_unfixable.rs index d765d7dab65c..282c72881d51 100644 --- a/tests/ui/needless_for_each_unfixable.rs +++ b/tests/ui/needless_for_each_unfixable.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_for_each)] -#![allow(clippy::needless_return)] +#![allow(clippy::needless_return, clippy::uninlined_format_args)] fn main() { let v: Vec = Vec::new(); diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed index fee8e3030b80..17f2227ba91c 100644 --- a/tests/ui/needless_late_init.fixed +++ b/tests/ui/needless_late_init.fixed @@ -1,12 +1,13 @@ // run-rustfix #![feature(let_chains)] +#![allow(unused)] #![allow( - unused, clippy::assign_op_pattern, clippy::blocks_in_if_conditions, clippy::let_and_return, clippy::let_unit_value, - clippy::nonminimal_bool + clippy::nonminimal_bool, + clippy::uninlined_format_args )] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 402d9f9ef7f8..d84457a29875 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,12 +1,13 @@ // run-rustfix #![feature(let_chains)] +#![allow(unused)] #![allow( - unused, clippy::assign_op_pattern, clippy::blocks_in_if_conditions, clippy::let_and_return, clippy::let_unit_value, - clippy::nonminimal_bool + clippy::nonminimal_bool, + clippy::uninlined_format_args )] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 313cdbbeba18..0a256fb4a131 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ error: unneeded late initialization - --> $DIR/needless_late_init.rs:23:5 + --> $DIR/needless_late_init.rs:24:5 | LL | let a; | ^^^^^^ created here @@ -13,7 +13,7 @@ LL | let a = "zero"; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:26:5 + --> $DIR/needless_late_init.rs:27:5 | LL | let b; | ^^^^^^ created here @@ -27,7 +27,7 @@ LL | let b = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:27:5 + --> $DIR/needless_late_init.rs:28:5 | LL | let c; | ^^^^^^ created here @@ -41,7 +41,7 @@ LL | let c = 2; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:31:5 + --> $DIR/needless_late_init.rs:32:5 | LL | let d: usize; | ^^^^^^^^^^^^^ created here @@ -54,7 +54,7 @@ LL | let d: usize = 1; | ~~~~~~~~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:34:5 + --> $DIR/needless_late_init.rs:35:5 | LL | let e; | ^^^^^^ created here @@ -67,7 +67,7 @@ LL | let e = format!("{}", d); | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:39:5 + --> $DIR/needless_late_init.rs:40:5 | LL | let a; | ^^^^^^ @@ -88,7 +88,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:48:5 + --> $DIR/needless_late_init.rs:49:5 | LL | let b; | ^^^^^^ @@ -109,7 +109,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:55:5 + --> $DIR/needless_late_init.rs:56:5 | LL | let d; | ^^^^^^ @@ -130,7 +130,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:63:5 + --> $DIR/needless_late_init.rs:64:5 | LL | let e; | ^^^^^^ @@ -151,7 +151,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:70:5 + --> $DIR/needless_late_init.rs:71:5 | LL | let f; | ^^^^^^ @@ -167,7 +167,7 @@ LL + 1 => "three", | error: unneeded late initialization - --> $DIR/needless_late_init.rs:76:5 + --> $DIR/needless_late_init.rs:77:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -187,7 +187,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:84:5 + --> $DIR/needless_late_init.rs:85:5 | LL | let x; | ^^^^^^ created here @@ -201,7 +201,7 @@ LL | let x = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:88:5 + --> $DIR/needless_late_init.rs:89:5 | LL | let x; | ^^^^^^ created here @@ -215,7 +215,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:92:5 + --> $DIR/needless_late_init.rs:93:5 | LL | let x; | ^^^^^^ created here @@ -229,7 +229,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:111:5 + --> $DIR/needless_late_init.rs:112:5 | LL | let a; | ^^^^^^ @@ -250,7 +250,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:128:5 + --> $DIR/needless_late_init.rs:129:5 | LL | let a; | ^^^^^^ diff --git a/tests/ui/needless_pass_by_value.rs b/tests/ui/needless_pass_by_value.rs index 5a35b100afe0..d79ad86b1948 100644 --- a/tests/ui/needless_pass_by_value.rs +++ b/tests/ui/needless_pass_by_value.rs @@ -1,10 +1,11 @@ #![warn(clippy::needless_pass_by_value)] +#![allow(dead_code)] #![allow( - dead_code, - clippy::single_match, - clippy::redundant_pattern_matching, clippy::option_option, - clippy::redundant_clone + clippy::redundant_clone, + clippy::redundant_pattern_matching, + clippy::single_match, + clippy::uninlined_format_args )] use std::borrow::Borrow; diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index 38f33c53f128..0e660a77dc0c 100644 --- a/tests/ui/needless_pass_by_value.stderr +++ b/tests/ui/needless_pass_by_value.stderr @@ -1,5 +1,5 @@ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:17:23 + --> $DIR/needless_pass_by_value.rs:18:23 | LL | fn foo(v: Vec, w: Vec, mut x: Vec, y: Vec) -> Vec { | ^^^^^^ help: consider changing the type to: `&[T]` @@ -7,55 +7,55 @@ LL | fn foo(v: Vec, w: Vec, mut x: Vec, y: Vec) -> Vec $DIR/needless_pass_by_value.rs:31:11 + --> $DIR/needless_pass_by_value.rs:32:11 | LL | fn bar(x: String, y: Wrapper) { | ^^^^^^ help: consider changing the type to: `&str` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:31:22 + --> $DIR/needless_pass_by_value.rs:32:22 | LL | fn bar(x: String, y: Wrapper) { | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:37:71 + --> $DIR/needless_pass_by_value.rs:38:71 | LL | fn test_borrow_trait, U: AsRef, V>(t: T, u: U, v: V) { | ^ help: consider taking a reference instead: `&V` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:49:18 + --> $DIR/needless_pass_by_value.rs:50:18 | LL | fn test_match(x: Option>, y: Option>) { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option>` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:62:24 + --> $DIR/needless_pass_by_value.rs:63:24 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:62:36 + --> $DIR/needless_pass_by_value.rs:63:36 | LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:78:49 + --> $DIR/needless_pass_by_value.rs:79:49 | LL | fn test_blanket_ref(_foo: T, _serializable: S) {} | ^ help: consider taking a reference instead: `&T` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:18 + --> $DIR/needless_pass_by_value.rs:81:18 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^ help: consider taking a reference instead: `&String` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:29 + --> $DIR/needless_pass_by_value.rs:81:29 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^ @@ -70,13 +70,13 @@ LL | let _ = t.to_string(); | ~~~~~~~~~~~~~ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:40 + --> $DIR/needless_pass_by_value.rs:81:40 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^^^ help: consider taking a reference instead: `&Vec` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:80:53 + --> $DIR/needless_pass_by_value.rs:81:53 | LL | fn issue_2114(s: String, t: String, u: Vec, v: Vec) { | ^^^^^^^^ @@ -91,85 +91,85 @@ LL | let _ = v.to_owned(); | ~~~~~~~~~~~~ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:93:12 + --> $DIR/needless_pass_by_value.rs:94:12 | LL | s: String, | ^^^^^^ help: consider changing the type to: `&str` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:94:12 + --> $DIR/needless_pass_by_value.rs:95:12 | LL | t: String, | ^^^^^^ help: consider taking a reference instead: `&String` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:103:23 + --> $DIR/needless_pass_by_value.rs:104:23 | LL | fn baz(&self, _u: U, _s: Self) {} | ^ help: consider taking a reference instead: `&U` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:103:30 + --> $DIR/needless_pass_by_value.rs:104:30 | LL | fn baz(&self, _u: U, _s: Self) {} | ^^^^ help: consider taking a reference instead: `&Self` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:125:24 + --> $DIR/needless_pass_by_value.rs:126:24 | LL | fn bar_copy(x: u32, y: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:131:29 + --> $DIR/needless_pass_by_value.rs:132:29 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:131:45 + --> $DIR/needless_pass_by_value.rs:132:45 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:131:61 + --> $DIR/needless_pass_by_value.rs:132:61 | LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | help: consider marking this type as `Copy` - --> $DIR/needless_pass_by_value.rs:123:1 + --> $DIR/needless_pass_by_value.rs:124:1 | LL | struct CopyWrapper(u32); | ^^^^^^^^^^^^^^^^^^ error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:143:40 + --> $DIR/needless_pass_by_value.rs:144:40 | LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} | ^ help: consider taking a reference instead: `&S` error: this argument is passed by value, but not consumed in the function body - --> $DIR/needless_pass_by_value.rs:148:20 + --> $DIR/needless_pass_by_value.rs:149:20 | LL | fn more_fun(_item: impl Club<'static, i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` diff --git a/tests/ui/needless_range_loop.rs b/tests/ui/needless_range_loop.rs index 3fce34367ae5..921801138a9b 100644 --- a/tests/ui/needless_range_loop.rs +++ b/tests/ui/needless_range_loop.rs @@ -1,4 +1,5 @@ #![warn(clippy::needless_range_loop)] +#![allow(clippy::uninlined_format_args)] static STATIC: [usize; 4] = [0, 1, 8, 16]; const CONST: [usize; 4] = [0, 1, 8, 16]; diff --git a/tests/ui/needless_range_loop.stderr b/tests/ui/needless_range_loop.stderr index a86cc69dfc5d..b31544ec334a 100644 --- a/tests/ui/needless_range_loop.stderr +++ b/tests/ui/needless_range_loop.stderr @@ -1,5 +1,5 @@ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:10:14 + --> $DIR/needless_range_loop.rs:11:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | for in &vec { | ~~~~~~ ~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:19:14 + --> $DIR/needless_range_loop.rs:20:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | for in &vec { | ~~~~~~ ~~~~ error: the loop variable `j` is only used to index `STATIC` - --> $DIR/needless_range_loop.rs:24:14 + --> $DIR/needless_range_loop.rs:25:14 | LL | for j in 0..4 { | ^^^^ @@ -33,7 +33,7 @@ LL | for in &STATIC { | ~~~~~~ ~~~~~~~ error: the loop variable `j` is only used to index `CONST` - --> $DIR/needless_range_loop.rs:28:14 + --> $DIR/needless_range_loop.rs:29:14 | LL | for j in 0..4 { | ^^^^ @@ -44,7 +44,7 @@ LL | for in &CONST { | ~~~~~~ ~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:32:14 + --> $DIR/needless_range_loop.rs:33:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | for (i, ) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec2` - --> $DIR/needless_range_loop.rs:40:14 + --> $DIR/needless_range_loop.rs:41:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | for in vec2.iter().take(vec.len()) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:44:14 + --> $DIR/needless_range_loop.rs:45:14 | LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | for in vec.iter().skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:48:14 + --> $DIR/needless_range_loop.rs:49:14 | LL | for i in 0..MAX_LEN { | ^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | for in vec.iter().take(MAX_LEN) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:52:14 + --> $DIR/needless_range_loop.rs:53:14 | LL | for i in 0..=MAX_LEN { | ^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | for in vec.iter().take(MAX_LEN + 1) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:56:14 + --> $DIR/needless_range_loop.rs:57:14 | LL | for i in 5..10 { | ^^^^^ @@ -110,7 +110,7 @@ LL | for in vec.iter().take(10).skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> $DIR/needless_range_loop.rs:60:14 + --> $DIR/needless_range_loop.rs:61:14 | LL | for i in 5..=10 { | ^^^^^^ @@ -121,7 +121,7 @@ LL | for in vec.iter().take(10 + 1).skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:64:14 + --> $DIR/needless_range_loop.rs:65:14 | LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | for (i, ) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:68:14 + --> $DIR/needless_range_loop.rs:69:14 | LL | for i in 5..10 { | ^^^^^ @@ -143,7 +143,7 @@ LL | for (i, ) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> $DIR/needless_range_loop.rs:73:14 + --> $DIR/needless_range_loop.rs:74:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 695883e8dff7..d2163b14fcad 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -232,4 +232,41 @@ fn issue_9361() -> i32 { return 1 + 2; } +fn issue8336(x: i32) -> bool { + if x > 0 { + println!("something"); + true + } else { + false + } +} + +fn issue8156(x: u8) -> u64 { + match x { + 80 => { + 10 + }, + _ => { + 100 + }, + } +} + +// Ideally the compiler should throw `unused_braces` in this case +fn issue9192() -> i32 { + { + 0 + } +} + +fn issue9503(x: usize) -> isize { + unsafe { + if x > 12 { + *(x as *const isize) + } else { + !*(x as *const isize) + } + } +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 63d9fe9ecdf8..114414b5fac7 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -232,4 +232,41 @@ fn issue_9361() -> i32 { return 1 + 2; } +fn issue8336(x: i32) -> bool { + if x > 0 { + println!("something"); + return true; + } else { + return false; + }; +} + +fn issue8156(x: u8) -> u64 { + match x { + 80 => { + return 10; + }, + _ => { + return 100; + }, + }; +} + +// Ideally the compiler should throw `unused_braces` in this case +fn issue9192() -> i32 { + { + return 0; + }; +} + +fn issue9503(x: usize) -> isize { + unsafe { + if x > 12 { + return *(x as *const isize); + } else { + return !*(x as *const isize); + }; + }; +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index cadee6e00dff..047fb6c2311a 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -2,225 +2,354 @@ error: unneeded `return` statement --> $DIR/needless_return.rs:26:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ | = note: `-D clippy::needless-return` implied by `-D warnings` + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:30:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:35:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:37:9 | LL | return false; - | ^^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:43:17 | LL | true => return false, - | ^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:45:13 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:52:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:54:16 | LL | let _ = || return true; - | ^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:58:5 | LL | return the_answer!(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:62:5 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:67:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:69:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:76:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with a unit value: `()` + | ^^^^^^ + | + = help: replace `return` with a unit value error: unneeded `return` statement --> $DIR/needless_return.rs:85:13 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:87:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with a unit value: `()` + | ^^^^^^ + | + = help: replace `return` with a unit value error: unneeded `return` statement --> $DIR/needless_return.rs:100:9 | LL | return String::from("test"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:102:9 | LL | return String::new(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:124:32 | LL | bar.unwrap_or_else(|_| return) - | ^^^^^^ help: replace `return` with an empty block: `{}` + | ^^^^^^ + | + = help: replace `return` with an empty block error: unneeded `return` statement --> $DIR/needless_return.rs:129:13 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:131:20 | LL | let _ = || return; - | ^^^^^^ help: replace `return` with an empty block: `{}` + | ^^^^^^ + | + = help: replace `return` with an empty block error: unneeded `return` statement --> $DIR/needless_return.rs:137:32 | LL | res.unwrap_or_else(|_| return Foo) - | ^^^^^^^^^^ help: remove `return`: `Foo` + | ^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:146:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:150:5 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:155:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:157:9 | LL | return false; - | ^^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:163:17 | LL | true => return false, - | ^^^^^^^^^^^^ help: remove `return`: `false` + | ^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:165:13 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:172:9 | LL | return true; - | ^^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:174:16 | LL | let _ = || return true; - | ^^^^^^^^^^^ help: remove `return`: `true` + | ^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:178:5 | LL | return the_answer!(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:182:5 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:187:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:189:9 | LL | return; - | ^^^^^^^ help: remove `return` + | ^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:196:14 | LL | _ => return, - | ^^^^^^ help: replace `return` with a unit value: `()` + | ^^^^^^ + | + = help: replace `return` with a unit value error: unneeded `return` statement --> $DIR/needless_return.rs:209:9 | LL | return String::from("test"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:211:9 | LL | return String::new(); - | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` error: unneeded `return` statement --> $DIR/needless_return.rs:227:5 | LL | return format!("Hello {}", "world!"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `format!("Hello {}", "world!")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:238:9 + | +LL | return true; + | ^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:240:9 + | +LL | return false; + | ^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:247:13 + | +LL | return 10; + | ^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:250:13 + | +LL | return 100; + | ^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:258:9 + | +LL | return 0; + | ^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:265:13 + | +LL | return *(x as *const isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:267:13 + | +LL | return !*(x as *const isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` -error: aborting due to 37 previous errors +error: aborting due to 44 previous errors diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 0a21589dd0d4..3dbef19890e9 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -203,6 +203,32 @@ pub fn test17() { }; } +// Issue #9356: `continue` in else branch of let..else +pub fn test18() { + let x = Some(0); + let y = 0; + // might loop + let _ = loop { + let Some(x) = x else { + if y > 0 { + continue; + } else { + return; + } + }; + + break x; + }; + // never loops + let _ = loop { + let Some(x) = x else { + return; + }; + + break x; + }; +} + fn main() { test1(); test2(); diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index f49b23924efe..3033f019244a 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -101,5 +101,18 @@ LL | | break 'label; LL | | } | |_________^ -error: aborting due to 9 previous errors +error: this loop never actually loops + --> $DIR/never_loop.rs:223:13 + | +LL | let _ = loop { + | _____________^ +LL | | let Some(x) = x else { +LL | | return; +LL | | }; +LL | | +LL | | break x; +LL | | }; + | |_____^ + +error: aborting due to 10 previous errors diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index fdefb11ae17a..f08eb092e6b2 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -1,9 +1,7 @@ #![feature(box_syntax, fn_traits, unboxed_closures)] #![warn(clippy::no_effect_underscore_binding)] -#![allow(dead_code)] -#![allow(path_statements)] -#![allow(clippy::deref_addrof)] -#![allow(clippy::redundant_field_names)] +#![allow(dead_code, path_statements)] +#![allow(clippy::deref_addrof, clippy::redundant_field_names, clippy::uninlined_format_args)] struct Unit; struct Tuple(i32); diff --git a/tests/ui/no_effect.stderr b/tests/ui/no_effect.stderr index 328d2555ceb8..6a1e636f9a61 100644 --- a/tests/ui/no_effect.stderr +++ b/tests/ui/no_effect.stderr @@ -1,5 +1,5 @@ error: statement with no effect - --> $DIR/no_effect.rs:94:5 + --> $DIR/no_effect.rs:92:5 | LL | 0; | ^^ @@ -7,157 +7,157 @@ LL | 0; = note: `-D clippy::no-effect` implied by `-D warnings` error: statement with no effect - --> $DIR/no_effect.rs:95:5 + --> $DIR/no_effect.rs:93:5 | LL | s2; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:96:5 + --> $DIR/no_effect.rs:94:5 | LL | Unit; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:97:5 + --> $DIR/no_effect.rs:95:5 | LL | Tuple(0); | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:98:5 + --> $DIR/no_effect.rs:96:5 | LL | Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:99:5 + --> $DIR/no_effect.rs:97:5 | LL | Struct { ..s }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:100:5 + --> $DIR/no_effect.rs:98:5 | LL | Union { a: 0 }; | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:101:5 + --> $DIR/no_effect.rs:99:5 | LL | Enum::Tuple(0); | ^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:102:5 + --> $DIR/no_effect.rs:100:5 | LL | Enum::Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:103:5 + --> $DIR/no_effect.rs:101:5 | LL | 5 + 6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:104:5 + --> $DIR/no_effect.rs:102:5 | LL | *&42; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:105:5 + --> $DIR/no_effect.rs:103:5 | LL | &6; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:106:5 + --> $DIR/no_effect.rs:104:5 | LL | (5, 6, 7); | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:107:5 + --> $DIR/no_effect.rs:105:5 | LL | box 42; | ^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:108:5 + --> $DIR/no_effect.rs:106:5 | LL | ..; | ^^^ error: statement with no effect - --> $DIR/no_effect.rs:109:5 + --> $DIR/no_effect.rs:107:5 | LL | 5..; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:110:5 + --> $DIR/no_effect.rs:108:5 | LL | ..5; | ^^^^ error: statement with no effect - --> $DIR/no_effect.rs:111:5 + --> $DIR/no_effect.rs:109:5 | LL | 5..6; | ^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:112:5 + --> $DIR/no_effect.rs:110:5 | LL | 5..=6; | ^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:113:5 + --> $DIR/no_effect.rs:111:5 | LL | [42, 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:114:5 + --> $DIR/no_effect.rs:112:5 | LL | [42, 55][1]; | ^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:115:5 + --> $DIR/no_effect.rs:113:5 | LL | (42, 55).1; | ^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:116:5 + --> $DIR/no_effect.rs:114:5 | LL | [42; 55]; | ^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:117:5 + --> $DIR/no_effect.rs:115:5 | LL | [42; 55][13]; | ^^^^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:119:5 + --> $DIR/no_effect.rs:117:5 | LL | || x += 5; | ^^^^^^^^^^ error: statement with no effect - --> $DIR/no_effect.rs:121:5 + --> $DIR/no_effect.rs:119:5 | LL | FooString { s: s }; | ^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:122:5 + --> $DIR/no_effect.rs:120:5 | LL | let _unused = 1; | ^^^^^^^^^^^^^^^^ @@ -165,19 +165,19 @@ LL | let _unused = 1; = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings` error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:123:5 + --> $DIR/no_effect.rs:121:5 | LL | let _penguin = || println!("Some helpful closure"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:124:5 + --> $DIR/no_effect.rs:122:5 | LL | let _duck = Struct { field: 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: binding to `_` prefixed variable with no side-effect - --> $DIR/no_effect.rs:125:5 + --> $DIR/no_effect.rs:123:5 | LL | let _cat = [2, 4, 6, 8][2]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 1290bd8efebd..00264dcceaa8 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index f3e5b62c65b7..f3363ebce54e 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index ab2a294a060f..0305387b9f8a 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:39:5 + --> $DIR/option_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -9,7 +9,7 @@ LL | x.field.map(do_nothing); = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:41:5 + --> $DIR/option_map_unit_fn_fixable.rs:40:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -17,7 +17,7 @@ LL | x.field.map(do_nothing); | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:43:5 + --> $DIR/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); | ^^^^^^^^^^^^^^^^^^^^- @@ -25,7 +25,7 @@ LL | x.field.map(diverge); | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:49:5 + --> $DIR/option_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -33,7 +33,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:51:5 + --> $DIR/option_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -41,7 +41,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:54:5 + --> $DIR/option_map_unit_fn_fixable.rs:53:5 | LL | x.field.map(|value| do_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -49,7 +49,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:56:5 + --> $DIR/option_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -57,7 +57,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:58:5 + --> $DIR/option_map_unit_fn_fixable.rs:57:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -65,7 +65,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:60:5 + --> $DIR/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -73,7 +73,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:63:5 + --> $DIR/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| diverge(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -81,7 +81,7 @@ LL | x.field.map(|value| diverge(value + captured)); | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:65:5 + --> $DIR/option_map_unit_fn_fixable.rs:64:5 | LL | x.field.map(|value| { diverge(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -89,7 +89,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:67:5 + --> $DIR/option_map_unit_fn_fixable.rs:66:5 | LL | x.field.map(|value| { diverge(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -97,7 +97,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:69:5 + --> $DIR/option_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -105,7 +105,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:74:5 + --> $DIR/option_map_unit_fn_fixable.rs:73:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -113,7 +113,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:76:5 + --> $DIR/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -121,7 +121,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:78:5 + --> $DIR/option_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -129,7 +129,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:81:5 + --> $DIR/option_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -137,7 +137,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:83:5 + --> $DIR/option_map_unit_fn_fixable.rs:82:5 | LL | option().map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^^- @@ -145,7 +145,7 @@ LL | option().map(do_nothing); | help: try this: `if let Some(a) = option() { do_nothing(a) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:85:5 + --> $DIR/option_map_unit_fn_fixable.rs:84:5 | LL | option().map(|value| println!("{:?}", value)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- diff --git a/tests/ui/option_take_on_temporary.fixed b/tests/ui/option_take_on_temporary.fixed deleted file mode 100644 index 29691e81666f..000000000000 --- a/tests/ui/option_take_on_temporary.fixed +++ /dev/null @@ -1,15 +0,0 @@ -// run-rustfix - -fn main() { - println!("Testing non erroneous option_take_on_temporary"); - let mut option = Some(1); - let _ = Box::new(move || option.take().unwrap()); - - println!("Testing non erroneous option_take_on_temporary"); - let x = Some(3); - x.as_ref(); - - println!("Testing erroneous option_take_on_temporary"); - let x = Some(3); - x.as_ref(); -} diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 5991188ab637..896430780ea8 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] +#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index c353b41e4495..2473163d4fd2 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -1,8 +1,7 @@ // run-rustfix - #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)] +#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index e3dab4cb1477..113ba150c619 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:49:22 + --> $DIR/or_fun_call.rs:48:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` @@ -7,151 +7,151 @@ LL | with_constructor.unwrap_or(make()); = note: `-D clippy::or-fun-call` implied by `-D warnings` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:52:14 + --> $DIR/or_fun_call.rs:51:14 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:55:21 + --> $DIR/or_fun_call.rs:54:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:58:14 + --> $DIR/or_fun_call.rs:57:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:61:19 + --> $DIR/or_fun_call.rs:60:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:64:24 + --> $DIR/or_fun_call.rs:63:24 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:67:23 + --> $DIR/or_fun_call.rs:66:23 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:70:18 + --> $DIR/or_fun_call.rs:69:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(::default)` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:73:18 + --> $DIR/or_fun_call.rs:72:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:76:14 + --> $DIR/or_fun_call.rs:75:14 | LL | with_vec.unwrap_or(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:79:21 + --> $DIR/or_fun_call.rs:78:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:82:19 + --> $DIR/or_fun_call.rs:81:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:85:23 + --> $DIR/or_fun_call.rs:84:23 | LL | map_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:88:21 + --> $DIR/or_fun_call.rs:87:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `or_insert` followed by a call to `new` - --> $DIR/or_fun_call.rs:91:25 + --> $DIR/or_fun_call.rs:90:25 | LL | btree_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try this: `or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:94:21 + --> $DIR/or_fun_call.rs:93:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:102:21 + --> $DIR/or_fun_call.rs:101:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:104:21 + --> $DIR/or_fun_call.rs:103:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:128:35 + --> $DIR/or_fun_call.rs:127:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:167:14 + --> $DIR/or_fun_call.rs:166:14 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:173:14 + --> $DIR/or_fun_call.rs:172:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:175:14 + --> $DIR/or_fun_call.rs:174:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:189:14 + --> $DIR/or_fun_call.rs:188:14 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:202:14 + --> $DIR/or_fun_call.rs:201:14 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:214:14 + --> $DIR/or_fun_call.rs:213:14 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:225:10 + --> $DIR/or_fun_call.rs:224:10 | LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` diff --git a/tests/ui/panic_in_result_fn_assertions.rs b/tests/ui/panic_in_result_fn_assertions.rs index ffdf8288adc7..08ab4d8681ed 100644 --- a/tests/ui/panic_in_result_fn_assertions.rs +++ b/tests/ui/panic_in_result_fn_assertions.rs @@ -1,5 +1,5 @@ #![warn(clippy::panic_in_result_fn)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] struct A; diff --git a/tests/ui/panic_in_result_fn_debug_assertions.rs b/tests/ui/panic_in_result_fn_debug_assertions.rs index c4fcd7e70944..df89d8c50246 100644 --- a/tests/ui/panic_in_result_fn_debug_assertions.rs +++ b/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -1,5 +1,5 @@ #![warn(clippy::panic_in_result_fn)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_wraps)] // debug_assert should never trigger the `panic_in_result_fn` lint diff --git a/tests/ui/patterns.fixed b/tests/ui/patterns.fixed index f22388154499..cd69014326eb 100644 --- a/tests/ui/patterns.fixed +++ b/tests/ui/patterns.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused)] #![warn(clippy::all)] +#![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn main() { let v = Some(true); diff --git a/tests/ui/patterns.rs b/tests/ui/patterns.rs index 5848ecd38d98..9128da420c0d 100644 --- a/tests/ui/patterns.rs +++ b/tests/ui/patterns.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused)] #![warn(clippy::all)] +#![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn main() { let v = Some(true); diff --git a/tests/ui/patterns.stderr b/tests/ui/patterns.stderr index af067580688b..2c46b4eb593e 100644 --- a/tests/ui/patterns.stderr +++ b/tests/ui/patterns.stderr @@ -1,5 +1,5 @@ error: the `y @ _` pattern can be written as just `y` - --> $DIR/patterns.rs:10:9 + --> $DIR/patterns.rs:11:9 | LL | y @ _ => (), | ^^^^^ help: try: `y` @@ -7,13 +7,13 @@ LL | y @ _ => (), = note: `-D clippy::redundant-pattern` implied by `-D warnings` error: the `x @ _` pattern can be written as just `x` - --> $DIR/patterns.rs:25:9 + --> $DIR/patterns.rs:26:9 | LL | ref mut x @ _ => { | ^^^^^^^^^^^^^ help: try: `ref mut x` error: the `x @ _` pattern can be written as just `x` - --> $DIR/patterns.rs:33:9 + --> $DIR/patterns.rs:34:9 | LL | ref x @ _ => println!("vec: {:?}", x), | ^^^^^^^^^ help: try: `ref x` diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 3f6639c14585..86f908f66b8f 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -1,4 +1,5 @@ #![warn(clippy::print_literal)] +#![allow(clippy::uninlined_format_args)] fn main() { // these should be fine diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index 23e6dbc3e341..6404dacdafa5 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -1,5 +1,5 @@ error: literal with an empty format string - --> $DIR/print_literal.rs:26:24 + --> $DIR/print_literal.rs:27:24 | LL | print!("Hello {}", "world"); | ^^^^^^^ @@ -12,7 +12,7 @@ LL + print!("Hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:27:36 + --> $DIR/print_literal.rs:28:36 | LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ @@ -24,7 +24,7 @@ LL + println!("Hello {} world", world); | error: literal with an empty format string - --> $DIR/print_literal.rs:28:26 + --> $DIR/print_literal.rs:29:26 | LL | println!("Hello {}", "world"); | ^^^^^^^ @@ -36,7 +36,7 @@ LL + println!("Hello world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:29:26 + --> $DIR/print_literal.rs:30:26 | LL | println!("{} {:.4}", "a literal", 5); | ^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + println!("a literal {:.4}", 5); | error: literal with an empty format string - --> $DIR/print_literal.rs:34:25 + --> $DIR/print_literal.rs:35:25 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ @@ -60,7 +60,7 @@ LL + println!("hello {1}", "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:34:34 + --> $DIR/print_literal.rs:35:34 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ @@ -72,7 +72,7 @@ LL + println!("{0} world", "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:35:34 + --> $DIR/print_literal.rs:36:34 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ @@ -84,7 +84,7 @@ LL + println!("world {0}", "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:35:25 + --> $DIR/print_literal.rs:36:25 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ @@ -96,7 +96,7 @@ LL + println!("{1} hello", "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:38:35 + --> $DIR/print_literal.rs:39:35 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ @@ -108,7 +108,7 @@ LL + println!("hello {bar}", bar = "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:38:50 + --> $DIR/print_literal.rs:39:50 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ @@ -120,7 +120,7 @@ LL + println!("{foo} world", foo = "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:39:50 + --> $DIR/print_literal.rs:40:50 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ @@ -132,7 +132,7 @@ LL + println!("world {foo}", foo = "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:39:35 + --> $DIR/print_literal.rs:40:35 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ diff --git a/tests/ui/ptr_offset_with_cast.fixed b/tests/ui/ptr_offset_with_cast.fixed index 718e391e8bf6..c57e2990fb95 100644 --- a/tests/ui/ptr_offset_with_cast.fixed +++ b/tests/ui/ptr_offset_with_cast.fixed @@ -1,4 +1,5 @@ // run-rustfix +#![allow(clippy::unnecessary_cast)] fn main() { let vec = vec![b'a', b'b', b'c']; diff --git a/tests/ui/ptr_offset_with_cast.rs b/tests/ui/ptr_offset_with_cast.rs index f613742c741e..3de7997acddd 100644 --- a/tests/ui/ptr_offset_with_cast.rs +++ b/tests/ui/ptr_offset_with_cast.rs @@ -1,4 +1,5 @@ // run-rustfix +#![allow(clippy::unnecessary_cast)] fn main() { let vec = vec![b'a', b'b', b'c']; diff --git a/tests/ui/ptr_offset_with_cast.stderr b/tests/ui/ptr_offset_with_cast.stderr index fd45224ca067..3ba40593d644 100644 --- a/tests/ui/ptr_offset_with_cast.stderr +++ b/tests/ui/ptr_offset_with_cast.stderr @@ -1,5 +1,5 @@ error: use of `offset` with a `usize` casted to an `isize` - --> $DIR/ptr_offset_with_cast.rs:12:17 + --> $DIR/ptr_offset_with_cast.rs:13:17 | LL | let _ = ptr.offset(offset_usize as isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` @@ -7,7 +7,7 @@ LL | let _ = ptr.offset(offset_usize as isize); = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` error: use of `wrapping_offset` with a `usize` casted to an `isize` - --> $DIR/ptr_offset_with_cast.rs:16:17 + --> $DIR/ptr_offset_with_cast.rs:17:17 | LL | let _ = ptr.wrapping_offset(offset_usize as isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 57f23bd1916f..993389232cc2 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -223,3 +223,12 @@ fn pattern() -> Result<(), PatternedError> { } fn main() {} + +// should not lint, `?` operator not available in const context +const fn issue9175(option: Option<()>) -> Option<()> { + if option.is_none() { + return None; + } + //stuff + Some(()) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 436f027c215d..9ae0d88829af 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -259,3 +259,12 @@ fn pattern() -> Result<(), PatternedError> { } fn main() {} + +// should not lint, `?` operator not available in const context +const fn issue9175(option: Option<()>) -> Option<()> { + if option.is_none() { + return None; + } + //stuff + Some(()) +} diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs index cb6ba36b14c8..b92490b4c523 100644 --- a/tests/ui/recursive_format_impl.rs +++ b/tests/ui/recursive_format_impl.rs @@ -1,9 +1,10 @@ #![warn(clippy::recursive_format_impl)] #![allow( + clippy::borrow_deref_ref, + clippy::deref_addrof, clippy::inherent_to_string_shadow_display, clippy::to_string_in_format_args, - clippy::deref_addrof, - clippy::borrow_deref_ref + clippy::uninlined_format_args )] use std::fmt; diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr index 84ce69df5669..8a58b9a3b178 100644 --- a/tests/ui/recursive_format_impl.stderr +++ b/tests/ui/recursive_format_impl.stderr @@ -1,5 +1,5 @@ error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion - --> $DIR/recursive_format_impl.rs:30:25 + --> $DIR/recursive_format_impl.rs:31:25 | LL | write!(f, "{}", self.to_string()) | ^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | write!(f, "{}", self.to_string()) = note: `-D clippy::recursive-format-impl` implied by `-D warnings` error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:74:9 + --> $DIR/recursive_format_impl.rs:75:9 | LL | write!(f, "{}", self) | ^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | write!(f, "{}", self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:83:9 + --> $DIR/recursive_format_impl.rs:84:9 | LL | write!(f, "{}", &self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | write!(f, "{}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:89:9 + --> $DIR/recursive_format_impl.rs:90:9 | LL | write!(f, "{:?}", &self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | write!(f, "{:?}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:98:9 + --> $DIR/recursive_format_impl.rs:99:9 | LL | write!(f, "{}", &&&self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | write!(f, "{}", &&&self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:172:9 + --> $DIR/recursive_format_impl.rs:173:9 | LL | write!(f, "{}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | write!(f, "{}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:178:9 + --> $DIR/recursive_format_impl.rs:179:9 | LL | write!(f, "{:?}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | write!(f, "{:?}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:194:9 + --> $DIR/recursive_format_impl.rs:195:9 | LL | write!(f, "{}", *self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | write!(f, "{}", *self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:210:9 + --> $DIR/recursive_format_impl.rs:211:9 | LL | write!(f, "{}", **&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | write!(f, "{}", **&&*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:226:9 + --> $DIR/recursive_format_impl.rs:227:9 | LL | write!(f, "{}", &&**&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index da52c0acf93b..00b427450935 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -1,8 +1,8 @@ // run-rustfix // rustfix-only-machine-applicable - #![feature(lint_reasons)] -#![allow(clippy::implicit_clone, clippy::drop_non_drop)] +#![allow(clippy::drop_non_drop, clippy::implicit_clone, clippy::uninlined_format_args)] + use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index 5867d019dbb7..f899127db8d0 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -1,8 +1,8 @@ // run-rustfix // rustfix-only-machine-applicable - #![feature(lint_reasons)] -#![allow(clippy::implicit_clone, clippy::drop_non_drop)] +#![allow(clippy::drop_non_drop, clippy::implicit_clone, clippy::uninlined_format_args)] + use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_pattern_matching_ipaddr.fixed b/tests/ui/redundant_pattern_matching_ipaddr.fixed index acc8de5f41ee..21bae909555c 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.fixed +++ b/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -1,8 +1,11 @@ // run-rustfix - -#![warn(clippy::all)] -#![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![warn(clippy::all, clippy::redundant_pattern_matching)] +#![allow(unused_must_use)] +#![allow( + clippy::match_like_matches_macro, + clippy::needless_bool, + clippy::uninlined_format_args +)] use std::net::{ IpAddr::{self, V4, V6}, diff --git a/tests/ui/redundant_pattern_matching_ipaddr.rs b/tests/ui/redundant_pattern_matching_ipaddr.rs index 678d91ce93ac..4dd9171677ec 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.rs +++ b/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -1,8 +1,11 @@ // run-rustfix - -#![warn(clippy::all)] -#![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![warn(clippy::all, clippy::redundant_pattern_matching)] +#![allow(unused_must_use)] +#![allow( + clippy::match_like_matches_macro, + clippy::needless_bool, + clippy::uninlined_format_args +)] use std::net::{ IpAddr::{self, V4, V6}, diff --git a/tests/ui/redundant_pattern_matching_ipaddr.stderr b/tests/ui/redundant_pattern_matching_ipaddr.stderr index caf458cd862e..536b589de54c 100644 --- a/tests/ui/redundant_pattern_matching_ipaddr.stderr +++ b/tests/ui/redundant_pattern_matching_ipaddr.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:14:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:17:12 | LL | if let V4(_) = &ipaddr {} | -------^^^^^---------- help: try this: `if ipaddr.is_ipv4()` @@ -7,31 +7,31 @@ LL | if let V4(_) = &ipaddr {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:16:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:19:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:18:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:21:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:20:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:23:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:22:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:25:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:32:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:35:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:37:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:40:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:42:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:45:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:47:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:50:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => true, @@ -67,49 +67,49 @@ LL | | }; | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:52:20 + --> $DIR/redundant_pattern_matching_ipaddr.rs:55:20 | LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:60:20 + --> $DIR/redundant_pattern_matching_ipaddr.rs:63:20 | LL | let _ = if let V4(_) = gen_ipaddr() { | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:62:19 + --> $DIR/redundant_pattern_matching_ipaddr.rs:65:19 | LL | } else if let V6(_) = gen_ipaddr() { | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:74:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:77:12 | LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:76:12 + --> $DIR/redundant_pattern_matching_ipaddr.rs:79:12 | LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:78:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:81:15 | LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:80:15 + --> $DIR/redundant_pattern_matching_ipaddr.rs:83:15 | LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` error: redundant pattern matching, consider using `is_ipv4()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:82:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:85:5 | LL | / match V4(Ipv4Addr::LOCALHOST) { LL | | V4(_) => true, @@ -118,7 +118,7 @@ LL | | }; | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` error: redundant pattern matching, consider using `is_ipv6()` - --> $DIR/redundant_pattern_matching_ipaddr.rs:87:5 + --> $DIR/redundant_pattern_matching_ipaddr.rs:90:5 | LL | / match V6(Ipv6Addr::LOCALHOST) { LL | | V4(_) => false, diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed index 83c783385efe..b88c5d0bec82 100644 --- a/tests/ui/redundant_pattern_matching_result.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -1,14 +1,13 @@ // run-rustfix - #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] +#![allow(deprecated, unused_must_use)] #![allow( - unused_must_use, - clippy::needless_bool, + clippy::if_same_then_else, clippy::match_like_matches_macro, - clippy::unnecessary_wraps, - deprecated, - clippy::if_same_then_else + clippy::needless_bool, + clippy::uninlined_format_args, + clippy::unnecessary_wraps )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs index e06d4485ae4f..5949cb2271c6 100644 --- a/tests/ui/redundant_pattern_matching_result.rs +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -1,14 +1,13 @@ // run-rustfix - #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] +#![allow(deprecated, unused_must_use)] #![allow( - unused_must_use, - clippy::needless_bool, + clippy::if_same_then_else, clippy::match_like_matches_macro, - clippy::unnecessary_wraps, - deprecated, - clippy::if_same_then_else + clippy::needless_bool, + clippy::uninlined_format_args, + clippy::unnecessary_wraps )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index d674d061e4dd..e6afe9eb78ea 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:16:12 + --> $DIR/redundant_pattern_matching_result.rs:15:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try this: `if result.is_ok()` @@ -7,31 +7,31 @@ LL | if let Ok(_) = &result {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:18:12 + --> $DIR/redundant_pattern_matching_result.rs:17:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:20:12 + --> $DIR/redundant_pattern_matching_result.rs:19:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:22:15 + --> $DIR/redundant_pattern_matching_result.rs:21:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:24:15 + --> $DIR/redundant_pattern_matching_result.rs:23:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:34:5 + --> $DIR/redundant_pattern_matching_result.rs:33:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:39:5 + --> $DIR/redundant_pattern_matching_result.rs:38:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:44:5 + --> $DIR/redundant_pattern_matching_result.rs:43:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:49:5 + --> $DIR/redundant_pattern_matching_result.rs:48:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -67,73 +67,73 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:54:20 + --> $DIR/redundant_pattern_matching_result.rs:53:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:60:20 + --> $DIR/redundant_pattern_matching_result.rs:59:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:62:19 + --> $DIR/redundant_pattern_matching_result.rs:61:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:85:19 + --> $DIR/redundant_pattern_matching_result.rs:84:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while (r#try!(result_opt())).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:86:16 + --> $DIR/redundant_pattern_matching_result.rs:85:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if (r#try!(result_opt())).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:92:12 + --> $DIR/redundant_pattern_matching_result.rs:91:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:93:15 + --> $DIR/redundant_pattern_matching_result.rs:92:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:111:12 + --> $DIR/redundant_pattern_matching_result.rs:110:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:113:12 + --> $DIR/redundant_pattern_matching_result.rs:112:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:115:15 + --> $DIR/redundant_pattern_matching_result.rs:114:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:117:15 + --> $DIR/redundant_pattern_matching_result.rs:116:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:119:5 + --> $DIR/redundant_pattern_matching_result.rs:118:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:124:5 + --> $DIR/redundant_pattern_matching_result.rs:123:5 | LL | / match Err::(42) { LL | | Ok(_) => false, diff --git a/tests/ui/result_map_unit_fn_fixable.fixed b/tests/ui/result_map_unit_fn_fixable.fixed index 14c331f67e73..d8b56237e983 100644 --- a/tests/ui/result_map_unit_fn_fixable.fixed +++ b/tests/ui/result_map_unit_fn_fixable.fixed @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::result_map_unit_fn)] #![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn do_nothing(_: T) {} diff --git a/tests/ui/result_map_unit_fn_fixable.rs b/tests/ui/result_map_unit_fn_fixable.rs index 8b0fca9ece1a..44f50d21109c 100644 --- a/tests/ui/result_map_unit_fn_fixable.rs +++ b/tests/ui/result_map_unit_fn_fixable.rs @@ -1,7 +1,7 @@ // run-rustfix - #![warn(clippy::result_map_unit_fn)] #![allow(unused)] +#![allow(clippy::uninlined_format_args)] fn do_nothing(_: T) {} diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed index 79e482eec303..c67edb36c67a 100644 --- a/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] const ANSWER: i32 = 42; diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs index b2e8bf33771a..0a4fef5bfe87 100644 --- a/tests/ui/reversed_empty_ranges_fixable.rs +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] const ANSWER: i32 = 42; diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 2d1bfe62c923..c2495ea95f97 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:9:5 + --> $DIR/reversed_empty_ranges_fixable.rs:10:5 | LL | (42..=21).for_each(|x| println!("{}", x)); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:10:13 + --> $DIR/reversed_empty_ranges_fixable.rs:11:13 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect:: $DIR/reversed_empty_ranges_fixable.rs:12:14 + --> $DIR/reversed_empty_ranges_fixable.rs:13:14 | LL | for _ in -21..=-42 {} | ^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for _ in (-42..=-21).rev() {} | ~~~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:13:14 + --> $DIR/reversed_empty_ranges_fixable.rs:14:14 | LL | for _ in 42u32..21u32 {} | ^^^^^^^^^^^^ diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.fixed b/tests/ui/reversed_empty_ranges_loops_fixable.fixed index f1503ed6d12f..78401e463d50 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_loops_fixable.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] fn main() { const MAX_LEN: usize = 42; diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.rs b/tests/ui/reversed_empty_ranges_loops_fixable.rs index a733788dc22c..f9e0f7fcd6db 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.rs +++ b/tests/ui/reversed_empty_ranges_loops_fixable.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] fn main() { const MAX_LEN: usize = 42; diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/tests/ui/reversed_empty_ranges_loops_fixable.stderr index a135da488ffd..dfc52e64c751 100644 --- a/tests/ui/reversed_empty_ranges_loops_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_loops_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:7:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:8:14 | LL | for i in 10..0 { | ^^^^^ @@ -11,7 +11,7 @@ LL | for i in (0..10).rev() { | ~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:11:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:12:14 | LL | for i in 10..=0 { | ^^^^^^ @@ -22,7 +22,7 @@ LL | for i in (0..=10).rev() { | ~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:15:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:16:14 | LL | for i in MAX_LEN..0 { | ^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for i in (0..MAX_LEN).rev() { | ~~~~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:34:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:35:14 | LL | for i in (10..0).map(|x| x * 2) { | ^^^^^^^ @@ -44,7 +44,7 @@ LL | for i in (0..10).rev().map(|x| x * 2) { | ~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:39:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:40:14 | LL | for i in 10..5 + 4 { | ^^^^^^^^^ @@ -55,7 +55,7 @@ LL | for i in (5 + 4..10).rev() { | ~~~~~~~~~~~~~~~~~ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_fixable.rs:43:14 + --> $DIR/reversed_empty_ranges_loops_fixable.rs:44:14 | LL | for i in (5 + 2)..(3 - 1) { | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/reversed_empty_ranges_loops_unfixable.rs b/tests/ui/reversed_empty_ranges_loops_unfixable.rs index c4c572244168..50264ef68cc0 100644 --- a/tests/ui/reversed_empty_ranges_loops_unfixable.rs +++ b/tests/ui/reversed_empty_ranges_loops_unfixable.rs @@ -1,4 +1,5 @@ #![warn(clippy::reversed_empty_ranges)] +#![allow(clippy::uninlined_format_args)] fn main() { for i in 5..5 { diff --git a/tests/ui/reversed_empty_ranges_loops_unfixable.stderr b/tests/ui/reversed_empty_ranges_loops_unfixable.stderr index 30095d20cfd4..4490ff35f5a6 100644 --- a/tests/ui/reversed_empty_ranges_loops_unfixable.stderr +++ b/tests/ui/reversed_empty_ranges_loops_unfixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_unfixable.rs:4:14 + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:5:14 | LL | for i in 5..5 { | ^^^^ @@ -7,7 +7,7 @@ LL | for i in 5..5 { = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_loops_unfixable.rs:8:14 + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:9:14 | LL | for i in (5 + 2)..(8 - 1) { | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index a48829caac01..e6198a1bc9a0 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -1,8 +1,14 @@ #![feature(adt_const_params)] -#![allow(incomplete_features)] #![warn(clippy::same_functions_in_if_condition)] -#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`. -#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks +// ifs_same_cond warning is different from `ifs_same_cond`. +// clippy::if_same_then_else, clippy::comparison_chain -- all empty blocks +#![allow(incomplete_features)] +#![allow( + clippy::comparison_chain, + clippy::if_same_then_else, + clippy::ifs_same_cond, + clippy::uninlined_format_args +)] fn function() -> bool { true diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr index 3901546cbd65..f352ade150ee 100644 --- a/tests/ui/same_functions_in_if_condition.stderr +++ b/tests/ui/same_functions_in_if_condition.stderr @@ -1,72 +1,72 @@ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:31:15 + --> $DIR/same_functions_in_if_condition.rs:37:15 | LL | } else if function() { | ^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:30:8 + --> $DIR/same_functions_in_if_condition.rs:36:8 | LL | if function() { | ^^^^^^^^^^ = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings` error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:36:15 + --> $DIR/same_functions_in_if_condition.rs:42:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:35:8 + --> $DIR/same_functions_in_if_condition.rs:41:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:41:15 + --> $DIR/same_functions_in_if_condition.rs:47:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:40:8 + --> $DIR/same_functions_in_if_condition.rs:46:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:46:15 + --> $DIR/same_functions_in_if_condition.rs:52:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:45:8 + --> $DIR/same_functions_in_if_condition.rs:51:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:53:15 + --> $DIR/same_functions_in_if_condition.rs:59:15 | LL | } else if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:51:8 + --> $DIR/same_functions_in_if_condition.rs:57:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:58:15 + --> $DIR/same_functions_in_if_condition.rs:64:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:56:8 + --> $DIR/same_functions_in_if_condition.rs:62:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs index c4dfbd9210e0..4ab7dbab59cf 100644 --- a/tests/ui/semicolon_if_nothing_returned.rs +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -1,5 +1,5 @@ #![warn(clippy::semicolon_if_nothing_returned)] -#![allow(clippy::redundant_closure)] +#![allow(clippy::redundant_closure, clippy::uninlined_format_args)] fn get_unit() {} diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.stderr index d2f41e3f934a..161dd66b086e 100644 --- a/tests/ui/should_impl_trait/method_list_1.stderr +++ b/tests/ui/should_impl_trait/method_list_1.stderr @@ -99,6 +99,16 @@ LL | | } | = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name +error: method `default` can be confused for the standard trait method `std::default::Default::default` + --> $DIR/method_list_1.rs:65:5 + | +LL | / pub fn default() -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::default::Default` or choosing a less ambiguous method name + error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` --> $DIR/method_list_1.rs:69:5 | @@ -139,5 +149,5 @@ LL | | } | = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index 84ecf1ea53ed..c65df9ece38c 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -1,11 +1,8 @@ // FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934 // // run-rustfix - #![warn(clippy::significant_drop_in_scrutinee)] -#![allow(clippy::single_match)] -#![allow(clippy::match_single_binding)] -#![allow(unused_assignments)] -#![allow(dead_code)] +#![allow(dead_code, unused_assignments)] +#![allow(clippy::match_single_binding, clippy::single_match, clippy::uninlined_format_args)] use std::num::ParseIntError; use std::ops::Deref; diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index f1ed808ba087..75063a8c987e 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -1,5 +1,5 @@ error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:59:11 + --> $DIR/significant_drop_in_scrutinee.rs:56:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:145:11 + --> $DIR/significant_drop_in_scrutinee.rs:142:11 | LL | match s.lock_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -38,7 +38,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:166:11 + --> $DIR/significant_drop_in_scrutinee.rs:163:11 | LL | match s.lock_m_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:214:11 + --> $DIR/significant_drop_in_scrutinee.rs:211:11 | LL | match counter.temp_increment().len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:237:16 + --> $DIR/significant_drop_in_scrutinee.rs:234:16 | LL | match (mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,7 +92,7 @@ LL ~ match (value, true) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:246:22 + --> $DIR/significant_drop_in_scrutinee.rs:243:22 | LL | match (true, mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL ~ match (true, value, true) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:256:16 + --> $DIR/significant_drop_in_scrutinee.rs:253:16 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL ~ match (value, true, mutex2.lock().unwrap().s.len()) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:256:54 + --> $DIR/significant_drop_in_scrutinee.rs:253:54 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -153,7 +153,7 @@ LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:267:15 + --> $DIR/significant_drop_in_scrutinee.rs:264:15 | LL | match mutex3.lock().unwrap().s.as_str() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:277:22 + --> $DIR/significant_drop_in_scrutinee.rs:274:22 | LL | match (true, mutex3.lock().unwrap().s.as_str()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -185,7 +185,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:296:11 + --> $DIR/significant_drop_in_scrutinee.rs:293:11 | LL | match mutex.lock().unwrap().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -204,7 +204,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:303:11 + --> $DIR/significant_drop_in_scrutinee.rs:300:11 | LL | match 1 < mutex.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -223,7 +223,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:321:11 + --> $DIR/significant_drop_in_scrutinee.rs:318:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +244,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:332:11 + --> $DIR/significant_drop_in_scrutinee.rs:329:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -265,7 +265,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:367:11 + --> $DIR/significant_drop_in_scrutinee.rs:364:11 | LL | match get_mutex_guard().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -284,7 +284,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:384:11 + --> $DIR/significant_drop_in_scrutinee.rs:381:11 | LL | match match i { | ___________^ @@ -316,7 +316,7 @@ LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:410:11 + --> $DIR/significant_drop_in_scrutinee.rs:407:11 | LL | match if i > 1 { | ___________^ @@ -349,7 +349,7 @@ LL ~ match value | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:464:11 + --> $DIR/significant_drop_in_scrutinee.rs:461:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -367,7 +367,7 @@ LL ~ match value { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:492:11 + --> $DIR/significant_drop_in_scrutinee.rs:489:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -380,7 +380,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:511:11 + --> $DIR/significant_drop_in_scrutinee.rs:508:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -399,7 +399,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:517:11 + --> $DIR/significant_drop_in_scrutinee.rs:514:11 | LL | match i = mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -418,7 +418,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:523:11 + --> $DIR/significant_drop_in_scrutinee.rs:520:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -437,7 +437,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:529:11 + --> $DIR/significant_drop_in_scrutinee.rs:526:11 | LL | match i += mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -456,7 +456,7 @@ LL ~ match () { | error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:592:11 + --> $DIR/significant_drop_in_scrutinee.rs:589:11 | LL | match rwlock.read().unwrap().to_number() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -467,7 +467,7 @@ LL | }; = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression - --> $DIR/significant_drop_in_scrutinee.rs:602:14 + --> $DIR/significant_drop_in_scrutinee.rs:599:14 | LL | for s in rwlock.read().unwrap().iter() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -478,7 +478,7 @@ LL | } = note: this might lead to deadlocks or other unexpected behavior error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression - --> $DIR/significant_drop_in_scrutinee.rs:617:11 + --> $DIR/significant_drop_in_scrutinee.rs:614:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index dd148edf5292..d0c9b7b5663e 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -1,4 +1,5 @@ #![warn(clippy::single_match)] +#![allow(clippy::uninlined_format_args)] fn dummy() {} diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 4d2b9ec5f903..7cecc1b73950 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:8:5 + --> $DIR/single_match.rs:9:5 | LL | / match x { LL | | Some(y) => { @@ -18,7 +18,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:16:5 + --> $DIR/single_match.rs:17:5 | LL | / match x { LL | | // Note the missing block braces. @@ -30,7 +30,7 @@ LL | | } | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:25:5 + --> $DIR/single_match.rs:26:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -39,7 +39,7 @@ LL | | }; | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:54:5 + --> $DIR/single_match.rs:55:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -48,7 +48,7 @@ LL | | }; | |_____^ help: try this: `if let Some(y) = x { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:59:5 + --> $DIR/single_match.rs:60:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -57,7 +57,7 @@ LL | | }; | |_____^ help: try this: `if let Ok(y) = y { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:66:5 + --> $DIR/single_match.rs:67:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -66,7 +66,7 @@ LL | | }; | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:87:5 + --> $DIR/single_match.rs:88:5 | LL | / match x { LL | | "test" => println!(), @@ -75,7 +75,7 @@ LL | | } | |_____^ help: try this: `if x == "test" { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:100:5 + --> $DIR/single_match.rs:101:5 | LL | / match x { LL | | Foo::A => println!(), @@ -84,7 +84,7 @@ LL | | } | |_____^ help: try this: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:106:5 + --> $DIR/single_match.rs:107:5 | LL | / match x { LL | | FOO_C => println!(), @@ -93,7 +93,7 @@ LL | | } | |_____^ help: try this: `if x == FOO_C { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:111:5 + --> $DIR/single_match.rs:112:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -102,7 +102,7 @@ LL | | } | |_____^ help: try this: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> $DIR/single_match.rs:117:5 + --> $DIR/single_match.rs:118:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -111,7 +111,7 @@ LL | | } | |_____^ help: try this: `if x == &Foo::A { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:134:5 + --> $DIR/single_match.rs:135:5 | LL | / match x { LL | | Bar::A => println!(), @@ -120,7 +120,7 @@ LL | | } | |_____^ help: try this: `if let Bar::A = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:142:5 + --> $DIR/single_match.rs:143:5 | LL | / match x { LL | | None => println!(), @@ -129,7 +129,7 @@ LL | | }; | |_____^ help: try this: `if let None = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:164:5 + --> $DIR/single_match.rs:165:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -138,7 +138,7 @@ LL | | } | |_____^ help: try this: `if let (Some(_), _) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:170:5 + --> $DIR/single_match.rs:171:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -147,7 +147,7 @@ LL | | } | |_____^ help: try this: `if let (Some(E::V), _) = x { todo!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:176:5 + --> $DIR/single_match.rs:177:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 70d6febb71f9..5d03f77e9326 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,8 +1,6 @@ // aux-build: proc_macro_with_span.rs - #![warn(clippy::single_match_else)] -#![allow(clippy::needless_return)] -#![allow(clippy::no_effect)] +#![allow(clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] extern crate proc_macro_with_span; use proc_macro_with_span::with_span; diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 38fd9c6a6782..62876a55dc61 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:19:13 + --> $DIR/single_match_else.rs:17:13 | LL | let _ = match ExprNode::Butterflies { | _____________^ @@ -21,7 +21,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:84:5 + --> $DIR/single_match_else.rs:82:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -41,7 +41,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:93:5 + --> $DIR/single_match_else.rs:91:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -61,7 +61,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:103:5 + --> $DIR/single_match_else.rs:101:5 | LL | / match Result::::Ok(1) { LL | | Ok(a) => println!("${:?}", a), @@ -81,7 +81,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:112:5 + --> $DIR/single_match_else.rs:110:5 | LL | / match Cow::from("moo") { LL | | Cow::Owned(a) => println!("${:?}", a), diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index 6b27475de4c8..75b114ba0aed 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -24,6 +24,12 @@ fn std_instead_of_core() { let cell_absolute = ::std::cell::Cell::new(8u32); let _ = std::env!("PATH"); + + // do not lint until `error_in_core` is stable + use std::error::Error; + + // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator` + use std::iter::Iterator; } #[warn(clippy::std_instead_of_alloc)] diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr index 8138ccb82a00..d2102497350b 100644 --- a/tests/ui/std_instead_of_core.stderr +++ b/tests/ui/std_instead_of_core.stderr @@ -63,9 +63,17 @@ LL | let cell_absolute = ::std::cell::Cell::new(8u32); | = help: consider importing the item from `core` -error: used import from `std` instead of `alloc` +error: used import from `std` instead of `core` --> $DIR/std_instead_of_core.rs:32:9 | +LL | use std::iter::Iterator; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider importing the item from `core` + +error: used import from `std` instead of `alloc` + --> $DIR/std_instead_of_core.rs:38:9 + | LL | use std::vec; | ^^^^^^^^ | @@ -73,7 +81,7 @@ LL | use std::vec; = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:33:9 + --> $DIR/std_instead_of_core.rs:39:9 | LL | use std::vec::Vec; | ^^^^^^^^^^^^^ @@ -81,7 +89,7 @@ LL | use std::vec::Vec; = help: consider importing the item from `alloc` error: used import from `alloc` instead of `core` - --> $DIR/std_instead_of_core.rs:38:9 + --> $DIR/std_instead_of_core.rs:44:9 | LL | use alloc::slice::from_ref; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -89,5 +97,5 @@ LL | use alloc::slice::from_ref; = help: consider importing the item from `core` = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/toplevel_ref_arg.fixed b/tests/ui/toplevel_ref_arg.fixed index b129d95c5602..09fb66ca37e0 100644 --- a/tests/ui/toplevel_ref_arg.fixed +++ b/tests/ui/toplevel_ref_arg.fixed @@ -1,7 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs - #![warn(clippy::toplevel_ref_arg)] +#![allow(clippy::uninlined_format_args)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/toplevel_ref_arg.rs b/tests/ui/toplevel_ref_arg.rs index 73eb4ff7306f..9d1f2f810983 100644 --- a/tests/ui/toplevel_ref_arg.rs +++ b/tests/ui/toplevel_ref_arg.rs @@ -1,7 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs - #![warn(clippy::toplevel_ref_arg)] +#![allow(clippy::uninlined_format_args)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index c0c64ebcabfb..af4f3b18443b 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -1,8 +1,11 @@ // normalize-stderr-test "\(\d+ byte\)" -> "(N byte)" // normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)" - #![deny(clippy::trivially_copy_pass_by_ref)] -#![allow(clippy::disallowed_names, clippy::redundant_field_names)] +#![allow( + clippy::disallowed_names, + clippy::redundant_field_names, + clippy::uninlined_format_args +)] #[derive(Copy, Clone)] struct Foo(u32); diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index 66ecb3d8e77a..6a8eca965534 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -1,113 +1,113 @@ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:47:11 + --> $DIR/trivially_copy_pass_by_ref.rs:50:11 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` | note: the lint level is defined here - --> $DIR/trivially_copy_pass_by_ref.rs:4:9 + --> $DIR/trivially_copy_pass_by_ref.rs:3:9 | LL | #![deny(clippy::trivially_copy_pass_by_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:47:20 + --> $DIR/trivially_copy_pass_by_ref.rs:50:20 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:47:29 + --> $DIR/trivially_copy_pass_by_ref.rs:50:29 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:12 + --> $DIR/trivially_copy_pass_by_ref.rs:57:12 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^^ help: consider passing by value instead: `self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:22 + --> $DIR/trivially_copy_pass_by_ref.rs:57:22 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:31 + --> $DIR/trivially_copy_pass_by_ref.rs:57:31 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:54:40 + --> $DIR/trivially_copy_pass_by_ref.rs:57:40 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:56:16 + --> $DIR/trivially_copy_pass_by_ref.rs:59:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:56:25 + --> $DIR/trivially_copy_pass_by_ref.rs:59:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:56:34 + --> $DIR/trivially_copy_pass_by_ref.rs:59:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:58:35 + --> $DIR/trivially_copy_pass_by_ref.rs:61:35 | LL | fn bad_issue7518(self, other: &Self) {} | ^^^^^ help: consider passing by value instead: `Self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:70:16 + --> $DIR/trivially_copy_pass_by_ref.rs:73:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:70:25 + --> $DIR/trivially_copy_pass_by_ref.rs:73:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:70:34 + --> $DIR/trivially_copy_pass_by_ref.rs:73:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:74:34 + --> $DIR/trivially_copy_pass_by_ref.rs:77:34 | LL | fn trait_method(&self, _foo: &Foo); | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:106:21 + --> $DIR/trivially_copy_pass_by_ref.rs:109:21 | LL | fn foo_never(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:111:15 + --> $DIR/trivially_copy_pass_by_ref.rs:114:15 | LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:138:37 + --> $DIR/trivially_copy_pass_by_ref.rs:141:37 | LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { | ^^^^^^^ help: consider passing by value instead: `u32` diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs index dc150cf28f2c..194e4fc157ef 100644 --- a/tests/ui/uninit_vec.rs +++ b/tests/ui/uninit_vec.rs @@ -91,4 +91,10 @@ fn main() { vec1.set_len(200); vec2.set_len(200); } + + // set_len(0) should not be detected + let mut vec: Vec = Vec::with_capacity(1000); + unsafe { + vec.set_len(0); + } } diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed new file mode 100644 index 000000000000..dcf10ed60a25 --- /dev/null +++ b/tests/ui/uninlined_format_args.fixed @@ -0,0 +1,164 @@ +// aux-build:proc_macro_with_span.rs +// run-rustfix +#![feature(custom_inner_attributes)] +#![warn(clippy::uninlined_format_args)] +#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] +#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] + +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + +macro_rules! no_param_str { + () => { + "{}" + }; +} + +macro_rules! my_println { + ($($args:tt),*) => {{ + println!($($args),*) + }}; +} + +macro_rules! my_println_args { + ($($args:tt),*) => {{ + println!("foo: {}", format_args!($($args),*)) + }}; +} + +fn tester(fn_arg: i32) { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + let width = 4; + let prec = 5; + let val = 6; + + // make sure this file hasn't been corrupted with tabs converted to spaces + // let _ = ' '; // <- this is a single tab character + let _: &[u8; 3] = b" "; // <- + + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); // 3 spaces + println!("val='{local_i32}'"); // tab + println!("val='{local_i32}'"); // space+tab + println!("val='{local_i32}'"); // tab+space + println!( + "val='{local_i32}'" + ); + println!("{local_i32}"); + println!("{fn_arg}"); + println!("{local_i32:?}"); + println!("{local_i32:#?}"); + println!("{local_i32:4}"); + println!("{local_i32:04}"); + println!("{local_i32:<3}"); + println!("{local_i32:#010x}"); + println!("{local_f64:.1}"); + println!("Hello {} is {local_f64:.local_i32$}", "x"); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("{local_i32} {local_f64}"); + println!("{local_i32}, {}", local_opt.unwrap()); + println!("{val}"); + println!("{val}"); + println!("{} {1}", local_i32, 42); + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); + println!("val='{fn_arg}'"); + println!("{local_i32}"); + println!("{local_i32:?}"); + println!("{local_i32:#?}"); + println!("{local_i32:04}"); + println!("{local_i32:<3}"); + println!("{local_i32:#010x}"); + println!("{local_f64:.1}"); + println!("{local_i32} {local_i32}"); + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); + println!("{local_i32} {local_f64}"); + println!("{local_f64} {local_i32}"); + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); + println!("{1} {0}", "str", local_i32); + println!("{local_i32}"); + println!("{local_i32:width$}"); + println!("{local_i32:width$}"); + println!("{local_i32:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{val:val$}"); + println!("{val:val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{width:width$}"); + println!("{local_i32:width$}"); + println!("{width:width$}"); + println!("{local_i32:width$}"); + println!("{prec:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{prec:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{width:width$.prec$}"); + println!("{width:width$.prec$}"); + println!("{local_f64:width$.prec$}"); + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); + println!( + "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", + ); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", + local_i32, + width, + prec, + 1 + 2 + ); + println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); + println!("{local_i32:width$.prec$}"); + println!("{width:width$.prec$}"); + println!("{}", format!("{local_i32}")); + my_println!("{}", local_i32); + my_println_args!("{}", local_i32); + + // these should NOT be modified by the lint + println!(concat!("nope ", "{}"), local_i32); + println!("val='{local_i32}'"); + println!("val='{local_i32 }'"); + println!("val='{local_i32 }'"); // with tab + println!("val='{local_i32\n}'"); + println!("{}", usize::MAX); + println!("{}", local_opt.unwrap()); + println!( + "val='{local_i32 + }'" + ); + println!(no_param_str!(), local_i32); + + println!( + "{val}", + ); + println!("{val}"); + + println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); + println!("{}", with_span!(span val)); +} + +fn main() { + tester(42); +} + +fn _under_msrv() { + #![clippy::msrv = "1.57"] + let local_i32 = 1; + println!("don't expand='{}'", local_i32); +} + +fn _meets_msrv() { + #![clippy::msrv = "1.58"] + let local_i32 = 1; + println!("expand='{local_i32}'"); +} diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs new file mode 100644 index 000000000000..924191f4324c --- /dev/null +++ b/tests/ui/uninlined_format_args.rs @@ -0,0 +1,169 @@ +// aux-build:proc_macro_with_span.rs +// run-rustfix +#![feature(custom_inner_attributes)] +#![warn(clippy::uninlined_format_args)] +#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] +#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] + +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + +macro_rules! no_param_str { + () => { + "{}" + }; +} + +macro_rules! my_println { + ($($args:tt),*) => {{ + println!($($args),*) + }}; +} + +macro_rules! my_println_args { + ($($args:tt),*) => {{ + println!("foo: {}", format_args!($($args),*)) + }}; +} + +fn tester(fn_arg: i32) { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + let width = 4; + let prec = 5; + let val = 6; + + // make sure this file hasn't been corrupted with tabs converted to spaces + // let _ = ' '; // <- this is a single tab character + let _: &[u8; 3] = b" "; // <- + + println!("val='{}'", local_i32); + println!("val='{ }'", local_i32); // 3 spaces + println!("val='{ }'", local_i32); // tab + println!("val='{ }'", local_i32); // space+tab + println!("val='{ }'", local_i32); // tab+space + println!( + "val='{ + }'", + local_i32 + ); + println!("{}", local_i32); + println!("{}", fn_arg); + println!("{:?}", local_i32); + println!("{:#?}", local_i32); + println!("{:4}", local_i32); + println!("{:04}", local_i32); + println!("{:<3}", local_i32); + println!("{:#010x}", local_i32); + println!("{:.1}", local_f64); + println!("Hello {} is {:.*}", "x", local_i32, local_f64); + println!("Hello {} is {:.*}", local_i32, 5, local_f64); + println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + println!("{} {}", local_i32, local_f64); + println!("{}, {}", local_i32, local_opt.unwrap()); + println!("{}", val); + println!("{}", v = val); + println!("{} {1}", local_i32, 42); + println!("val='{\t }'", local_i32); + println!("val='{\n }'", local_i32); + println!("val='{local_i32}'", local_i32 = local_i32); + println!("val='{local_i32}'", local_i32 = fn_arg); + println!("{0}", local_i32); + println!("{0:?}", local_i32); + println!("{0:#?}", local_i32); + println!("{0:04}", local_i32); + println!("{0:<3}", local_i32); + println!("{0:#010x}", local_i32); + println!("{0:.1}", local_f64); + println!("{0} {0}", local_i32); + println!("{1} {} {0} {}", local_i32, local_f64); + println!("{0} {1}", local_i32, local_f64); + println!("{1} {0}", local_i32, local_f64); + println!("{1} {0} {1} {0}", local_i32, local_f64); + println!("{1} {0}", "str", local_i32); + println!("{v}", v = local_i32); + println!("{local_i32:0$}", width); + println!("{local_i32:w$}", w = width); + println!("{local_i32:.0$}", prec); + println!("{local_i32:.p$}", p = prec); + println!("{:0$}", v = val); + println!("{0:0$}", v = val); + println!("{:0$.0$}", v = val); + println!("{0:0$.0$}", v = val); + println!("{0:0$.v$}", v = val); + println!("{0:v$.0$}", v = val); + println!("{v:0$.0$}", v = val); + println!("{v:v$.0$}", v = val); + println!("{v:0$.v$}", v = val); + println!("{v:v$.v$}", v = val); + println!("{:0$}", width); + println!("{:1$}", local_i32, width); + println!("{:w$}", w = width); + println!("{:w$}", local_i32, w = width); + println!("{:.0$}", prec); + println!("{:.1$}", local_i32, prec); + println!("{:.p$}", p = prec); + println!("{:.p$}", local_i32, p = prec); + println!("{:0$.1$}", width, prec); + println!("{:0$.w$}", width, w = prec); + println!("{:1$.2$}", local_f64, width, prec); + println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", + local_i32, width, prec, + ); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", + local_i32, + width, + prec, + 1 + 2 + ); + println!("Width = {}, value with width = {:0$}", local_i32, local_f64); + println!("{:w$.p$}", local_i32, w = width, p = prec); + println!("{:w$.p$}", w = width, p = prec); + println!("{}", format!("{}", local_i32)); + my_println!("{}", local_i32); + my_println_args!("{}", local_i32); + + // these should NOT be modified by the lint + println!(concat!("nope ", "{}"), local_i32); + println!("val='{local_i32}'"); + println!("val='{local_i32 }'"); + println!("val='{local_i32 }'"); // with tab + println!("val='{local_i32\n}'"); + println!("{}", usize::MAX); + println!("{}", local_opt.unwrap()); + println!( + "val='{local_i32 + }'" + ); + println!(no_param_str!(), local_i32); + + println!( + "{}", + // comment with a comma , in it + val, + ); + println!("{}", /* comment with a comma , in it */ val); + + println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); + println!("{}", with_span!(span val)); +} + +fn main() { + tester(42); +} + +fn _under_msrv() { + #![clippy::msrv = "1.57"] + let local_i32 = 1; + println!("don't expand='{}'", local_i32); +} + +fn _meets_msrv() { + #![clippy::msrv = "1.58"] + let local_i32 = 1; + println!("expand='{}'", local_i32); +} diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr new file mode 100644 index 000000000000..1b4dada28dac --- /dev/null +++ b/tests/ui/uninlined_format_args.stderr @@ -0,0 +1,894 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:41:5 + | +LL | println!("val='{}'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:42:5 + | +LL | println!("val='{ }'", local_i32); // 3 spaces + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // 3 spaces +LL + println!("val='{local_i32}'"); // 3 spaces + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:43:5 + | +LL | println!("val='{ }'", local_i32); // tab + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // tab +LL + println!("val='{local_i32}'"); // tab + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:44:5 + | +LL | println!("val='{ }'", local_i32); // space+tab + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // space+tab +LL + println!("val='{local_i32}'"); // space+tab + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:45:5 + | +LL | println!("val='{ }'", local_i32); // tab+space + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // tab+space +LL + println!("val='{local_i32}'"); // tab+space + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:46:5 + | +LL | / println!( +LL | | "val='{ +LL | | }'", +LL | | local_i32 +LL | | ); + | |_____^ + | +help: change this to + | +LL - "val='{ +LL + "val='{local_i32}'" + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:51:5 + | +LL | println!("{}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:52:5 + | +LL | println!("{}", fn_arg); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", fn_arg); +LL + println!("{fn_arg}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:53:5 + | +LL | println!("{:?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:?}", local_i32); +LL + println!("{local_i32:?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:54:5 + | +LL | println!("{:#?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:#?}", local_i32); +LL + println!("{local_i32:#?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:55:5 + | +LL | println!("{:4}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:4}", local_i32); +LL + println!("{local_i32:4}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:56:5 + | +LL | println!("{:04}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:04}", local_i32); +LL + println!("{local_i32:04}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:57:5 + | +LL | println!("{:<3}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:<3}", local_i32); +LL + println!("{local_i32:<3}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:58:5 + | +LL | println!("{:#010x}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:#010x}", local_i32); +LL + println!("{local_i32:#010x}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:59:5 + | +LL | println!("{:.1}", local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.1}", local_f64); +LL + println!("{local_f64:.1}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:60:5 + | +LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); +LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:61:5 + | +LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:62:5 + | +LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:63:5 + | +LL | println!("{} {}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{} {}", local_i32, local_f64); +LL + println!("{local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:64:5 + | +LL | println!("{}, {}", local_i32, local_opt.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}, {}", local_i32, local_opt.unwrap()); +LL + println!("{local_i32}, {}", local_opt.unwrap()); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:65:5 + | +LL | println!("{}", val); + | ^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:66:5 + | +LL | println!("{}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", v = val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:68:5 + | +LL | println!("val='{/t }'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{/t }'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:69:5 + | +LL | println!("val='{/n }'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{/n }'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:70:5 + | +LL | println!("val='{local_i32}'", local_i32 = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{local_i32}'", local_i32 = local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:71:5 + | +LL | println!("val='{local_i32}'", local_i32 = fn_arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{local_i32}'", local_i32 = fn_arg); +LL + println!("val='{fn_arg}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:72:5 + | +LL | println!("{0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0}", local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:73:5 + | +LL | println!("{0:?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:?}", local_i32); +LL + println!("{local_i32:?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:74:5 + | +LL | println!("{0:#?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:#?}", local_i32); +LL + println!("{local_i32:#?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:75:5 + | +LL | println!("{0:04}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:04}", local_i32); +LL + println!("{local_i32:04}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:76:5 + | +LL | println!("{0:<3}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:<3}", local_i32); +LL + println!("{local_i32:<3}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:77:5 + | +LL | println!("{0:#010x}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:#010x}", local_i32); +LL + println!("{local_i32:#010x}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:78:5 + | +LL | println!("{0:.1}", local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:.1}", local_f64); +LL + println!("{local_f64:.1}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:79:5 + | +LL | println!("{0} {0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0} {0}", local_i32); +LL + println!("{local_i32} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:80:5 + | +LL | println!("{1} {} {0} {}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {} {0} {}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:81:5 + | +LL | println!("{0} {1}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0} {1}", local_i32, local_f64); +LL + println!("{local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:82:5 + | +LL | println!("{1} {0}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {0}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:83:5 + | +LL | println!("{1} {0} {1} {0}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {0} {1} {0}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:85:5 + | +LL | println!("{v}", v = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v}", v = local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:86:5 + | +LL | println!("{local_i32:0$}", width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:0$}", width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:87:5 + | +LL | println!("{local_i32:w$}", w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:w$}", w = width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:88:5 + | +LL | println!("{local_i32:.0$}", prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:.0$}", prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:89:5 + | +LL | println!("{local_i32:.p$}", p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:.p$}", p = prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:90:5 + | +LL | println!("{:0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$}", v = val); +LL + println!("{val:val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:91:5 + | +LL | println!("{0:0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$}", v = val); +LL + println!("{val:val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:92:5 + | +LL | println!("{:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:93:5 + | +LL | println!("{0:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:94:5 + | +LL | println!("{0:0$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:95:5 + | +LL | println!("{0:v$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:v$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:96:5 + | +LL | println!("{v:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:97:5 + | +LL | println!("{v:v$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:v$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:98:5 + | +LL | println!("{v:0$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:0$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:99:5 + | +LL | println!("{v:v$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:v$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:100:5 + | +LL | println!("{:0$}", width); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$}", width); +LL + println!("{width:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:101:5 + | +LL | println!("{:1$}", local_i32, width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$}", local_i32, width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:102:5 + | +LL | println!("{:w$}", w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$}", w = width); +LL + println!("{width:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:103:5 + | +LL | println!("{:w$}", local_i32, w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$}", local_i32, w = width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:104:5 + | +LL | println!("{:.0$}", prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.0$}", prec); +LL + println!("{prec:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:105:5 + | +LL | println!("{:.1$}", local_i32, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.1$}", local_i32, prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:106:5 + | +LL | println!("{:.p$}", p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.p$}", p = prec); +LL + println!("{prec:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:107:5 + | +LL | println!("{:.p$}", local_i32, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.p$}", local_i32, p = prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:108:5 + | +LL | println!("{:0$.1$}", width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.1$}", width, prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:109:5 + | +LL | println!("{:0$.w$}", width, w = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.w$}", width, w = prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:110:5 + | +LL | println!("{:1$.2$}", local_f64, width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$.2$}", local_f64, width, prec); +LL + println!("{local_f64:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:111:5 + | +LL | println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); +LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:112:5 + | +LL | / println!( +LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", +LL | | local_i32, width, prec, +LL | | ); + | |_____^ + | +help: change this to + | +LL ~ "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, +LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:123:5 + | +LL | println!("Width = {}, value with width = {:0$}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Width = {}, value with width = {:0$}", local_i32, local_f64); +LL + println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:124:5 + | +LL | println!("{:w$.p$}", local_i32, w = width, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$.p$}", local_i32, w = width, p = prec); +LL + println!("{local_i32:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:125:5 + | +LL | println!("{:w$.p$}", w = width, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$.p$}", w = width, p = prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:126:20 + | +LL | println!("{}", format!("{}", local_i32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", format!("{}", local_i32)); +LL + println!("{}", format!("{local_i32}")); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:144:5 + | +LL | / println!( +LL | | "{}", +LL | | // comment with a comma , in it +LL | | val, +LL | | ); + | |_____^ + | +help: change this to + | +LL - "{}", +LL + "{val}", + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:149:5 + | +LL | println!("{}", /* comment with a comma , in it */ val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", /* comment with a comma , in it */ val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:168:5 + | +LL | println!("expand='{}'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("expand='{}'", local_i32); +LL + println!("expand='{local_i32}'"); + | + +error: aborting due to 73 previous errors + diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 7bf3adc07ac5..07e70873a813 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,17 +1,16 @@ // aux-build: proc_macro_with_span.rs - #![warn(clippy::unit_arg)] +#![allow(unused_must_use, unused_variables)] #![allow( + clippy::let_unit_value, + clippy::needless_question_mark, + clippy::never_loop, clippy::no_effect, - unused_must_use, - unused_variables, - clippy::unused_unit, - clippy::unnecessary_wraps, clippy::or_fun_call, - clippy::needless_question_mark, clippy::self_named_constructors, - clippy::let_unit_value, - clippy::never_loop + clippy::uninlined_format_args, + clippy::unnecessary_wraps, + clippy::unused_unit )] extern crate proc_macro_with_span; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 1de9d44bb0d6..74d4d2f4052f 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:63:5 + --> $DIR/unit_arg.rs:62:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:66:5 + --> $DIR/unit_arg.rs:65:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:67:5 + --> $DIR/unit_arg.rs:66:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:72:5 + --> $DIR/unit_arg.rs:71:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL ~ b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:75:5 + --> $DIR/unit_arg.rs:74:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:76:5 + --> $DIR/unit_arg.rs:75:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:80:5 + --> $DIR/unit_arg.rs:79:5 | LL | / taking_multiple_units( LL | | { @@ -146,7 +146,7 @@ LL ~ ); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:91:13 + --> $DIR/unit_arg.rs:90:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -160,7 +160,7 @@ LL ~ }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:94:5 + --> $DIR/unit_arg.rs:93:5 | LL | foo(foo(())); | ^^^^^^^^^^^^ @@ -172,7 +172,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:131:5 + --> $DIR/unit_arg.rs:130:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/tests/ui/unit_arg_empty_blocks.fixed b/tests/ui/unit_arg_empty_blocks.fixed index 9400e93cac83..5787471a32ca 100644 --- a/tests/ui/unit_arg_empty_blocks.fixed +++ b/tests/ui/unit_arg_empty_blocks.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow(unused_must_use, unused_variables)] +#![allow(clippy::no_effect, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/unit_arg_empty_blocks.rs b/tests/ui/unit_arg_empty_blocks.rs index 5f52b6c5315f..6a42c2ccf42b 100644 --- a/tests/ui/unit_arg_empty_blocks.rs +++ b/tests/ui/unit_arg_empty_blocks.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow(unused_must_use, unused_variables)] +#![allow(clippy::no_effect, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index d35e931697d2..c697dfb1efa2 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:16:5 + --> $DIR/unit_arg_empty_blocks.rs:17:5 | LL | foo({}); | ^^^^--^ @@ -9,7 +9,7 @@ LL | foo({}); = note: `-D clippy::unit-arg` implied by `-D warnings` error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:17:5 + --> $DIR/unit_arg_empty_blocks.rs:18:5 | LL | foo3({}, 2, 2); | ^^^^^--^^^^^^^ @@ -17,7 +17,7 @@ LL | foo3({}, 2, 2); | help: use a unit literal instead: `()` error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:18:5 + --> $DIR/unit_arg_empty_blocks.rs:19:5 | LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL ~ taking_two_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:19:5 + --> $DIR/unit_arg_empty_blocks.rs:20:5 | LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index ee9f157342d4..94dc96427263 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -97,4 +97,18 @@ mod fixable { let _ = -(1 + 1) as i64; } + + fn issue_9563() { + let _: f64 = (-8.0_f64).exp(); + #[allow(clippy::precedence)] + let _: f64 = -8.0_f64.exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior + } + + fn issue_9562_non_literal() { + fn foo() -> f32 { + 0. + } + + let _num = foo(); + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 5b70412424c0..e5150256f69a 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -97,4 +97,18 @@ mod fixable { let _ = -(1 + 1) as i64; } + + fn issue_9563() { + let _: f64 = (-8.0 as f64).exp(); + #[allow(clippy::precedence)] + let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior + } + + fn issue_9562_non_literal() { + fn foo() -> f32 { + 0. + } + + let _num = foo() as f32; + } } diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index f7829ff3b0ef..e5c3dd5e53f8 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -162,5 +162,23 @@ error: casting integer literal to `i64` is unnecessary LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` -error: aborting due to 27 previous errors +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:102:22 + | +LL | let _: f64 = (-8.0 as f64).exp(); + | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:104:23 + | +LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior + | ^^^^^^^^^^^^ help: try: `8.0_f64` + +error: casting to the same type is unnecessary (`f32` -> `f32`) + --> $DIR/unnecessary_cast.rs:112:20 + | +LL | let _num = foo() as f32; + | ^^^^^^^^^^^^ help: try: `foo()` + +error: aborting due to 30 previous errors diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index 6770a7fac90f..8b1629b19a76 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -1,7 +1,7 @@ // does not test any rustfixable lints - #![warn(clippy::clone_on_ref_ptr)] -#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wraps)] +#![allow(unused)] +#![allow(clippy::redundant_clone, clippy::uninlined_format_args, clippy::unnecessary_wraps)] use std::cell::RefCell; use std::rc::{self, Rc}; diff --git a/tests/ui/unnecessary_join.fixed b/tests/ui/unnecessary_join.fixed index 7e12c6ae4be9..347953960257 100644 --- a/tests/ui/unnecessary_join.fixed +++ b/tests/ui/unnecessary_join.fixed @@ -1,6 +1,6 @@ // run-rustfix - #![warn(clippy::unnecessary_join)] +#![allow(clippy::uninlined_format_args)] fn main() { // should be linted diff --git a/tests/ui/unnecessary_join.rs b/tests/ui/unnecessary_join.rs index 0a21656a7558..344918cd2a2e 100644 --- a/tests/ui/unnecessary_join.rs +++ b/tests/ui/unnecessary_join.rs @@ -1,6 +1,6 @@ // run-rustfix - #![warn(clippy::unnecessary_join)] +#![allow(clippy::uninlined_format_args)] fn main() { // should be linted diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index eed817968832..ce4a82e02174 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -1,9 +1,13 @@ // run-rustfix +// aux-build: proc_macro_with_span.rs #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::map_identity)] +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + struct Deep(Option); #[derive(Copy, Clone)] @@ -21,6 +25,14 @@ fn some_call() -> T { T::default() } +struct Issue9427(i32); + +impl Drop for Issue9427 { + fn drop(&mut self) { + println!("{}", self.0); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -73,6 +85,9 @@ fn main() { let _ = deep.0.or_else(|| some_call()); let _ = opt.ok_or_else(|| ext_arr[0]); + // Should not lint - bool + let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); @@ -130,3 +145,9 @@ fn main() { let _: Result = res.and_then(|x| Err(x)); let _: Result = res.or_else(|err| Ok(err)); } + +#[allow(unused)] +fn issue9485() { + // should not lint, is in proc macro + with_span!(span Some(42).unwrap_or_else(|| 2);); +} diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 1588db79b38a..59cdf6628546 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -1,9 +1,13 @@ // run-rustfix +// aux-build: proc_macro_with_span.rs #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] #![allow(clippy::map_identity)] +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + struct Deep(Option); #[derive(Copy, Clone)] @@ -21,6 +25,14 @@ fn some_call() -> T { T::default() } +struct Issue9427(i32); + +impl Drop for Issue9427 { + fn drop(&mut self) { + println!("{}", self.0); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -73,6 +85,9 @@ fn main() { let _ = deep.0.or_else(|| some_call()); let _ = opt.ok_or_else(|| ext_arr[0]); + // Should not lint - bool + let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); @@ -130,3 +145,9 @@ fn main() { let _: Result = res.and_then(|x| Err(x)); let _: Result = res.or_else(|err| Ok(err)); } + +#[allow(unused)] +fn issue9485() { + // should not lint, is in proc macro + with_span!(span Some(42).unwrap_or_else(|| 2);); +} diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 83dc7fd832c3..8a9ece4aa7e5 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:48:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^-------------------- @@ -9,7 +9,7 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 + --> $DIR/unnecessary_lazy_eval.rs:49:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^--------------------------------- @@ -17,7 +17,7 @@ LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:38:13 + --> $DIR/unnecessary_lazy_eval.rs:50:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^------------------------------------- @@ -25,7 +25,7 @@ LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:40:13 + --> $DIR/unnecessary_lazy_eval.rs:52:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^--------------------- @@ -33,7 +33,7 @@ LL | let _ = opt.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:41:13 + --> $DIR/unnecessary_lazy_eval.rs:53:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^------------------- @@ -41,7 +41,7 @@ LL | let _ = opt.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:42:13 + --> $DIR/unnecessary_lazy_eval.rs:54:13 | LL | let _ = opt.or_else(|| None); | ^^^^---------------- @@ -49,7 +49,7 @@ LL | let _ = opt.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^------------------------ @@ -57,7 +57,7 @@ LL | let _ = opt.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:44:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^---------------- @@ -65,7 +65,7 @@ LL | let _ = opt.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:45:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | ^^^^^^^^^^^^^^^^^------------------------------- @@ -73,7 +73,7 @@ LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:46:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = cond.then(|| astronomers_pi); | ^^^^^----------------------- @@ -81,7 +81,7 @@ LL | let _ = cond.then(|| astronomers_pi); | help: use `then_some(..)` instead: `then_some(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:61:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^-------------------- @@ -89,7 +89,7 @@ LL | let _ = Some(10).unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:13 + --> $DIR/unnecessary_lazy_eval.rs:62:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^--------------------- @@ -97,7 +97,7 @@ LL | let _ = Some(10).and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:28 + --> $DIR/unnecessary_lazy_eval.rs:63:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^------------------- @@ -105,7 +105,7 @@ LL | let _: Option = None.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:64:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^------------------------ @@ -113,7 +113,7 @@ LL | let _ = None.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:53:35 + --> $DIR/unnecessary_lazy_eval.rs:65:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^---------------- @@ -121,7 +121,7 @@ LL | let _: Result = None.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:28 + --> $DIR/unnecessary_lazy_eval.rs:66:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^---------------- @@ -129,7 +129,7 @@ LL | let _: Option = None.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:57:13 + --> $DIR/unnecessary_lazy_eval.rs:69:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^-------------------- @@ -137,7 +137,7 @@ LL | let _ = deep.0.unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:58:13 + --> $DIR/unnecessary_lazy_eval.rs:70:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^--------------------- @@ -145,7 +145,7 @@ LL | let _ = deep.0.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:59:13 + --> $DIR/unnecessary_lazy_eval.rs:71:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^---------------- @@ -153,7 +153,7 @@ LL | let _ = deep.0.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:60:13 + --> $DIR/unnecessary_lazy_eval.rs:72:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^------------------------ @@ -161,7 +161,7 @@ LL | let _ = deep.0.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:61:13 + --> $DIR/unnecessary_lazy_eval.rs:73:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^---------------- @@ -169,7 +169,7 @@ LL | let _ = deep.0.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:81:28 + --> $DIR/unnecessary_lazy_eval.rs:96:28 | LL | let _: Option = None.or_else(|| Some(3)); | ^^^^^------------------- @@ -177,7 +177,7 @@ LL | let _: Option = None.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:82:13 + --> $DIR/unnecessary_lazy_eval.rs:97:13 | LL | let _ = deep.0.or_else(|| Some(3)); | ^^^^^^^------------------- @@ -185,7 +185,7 @@ LL | let _ = deep.0.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:83:13 + --> $DIR/unnecessary_lazy_eval.rs:98:13 | LL | let _ = opt.or_else(|| Some(3)); | ^^^^------------------- @@ -193,7 +193,7 @@ LL | let _ = opt.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:89:13 + --> $DIR/unnecessary_lazy_eval.rs:104:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^--------------------- @@ -201,7 +201,7 @@ LL | let _ = res2.unwrap_or_else(|_| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:90:13 + --> $DIR/unnecessary_lazy_eval.rs:105:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^---------------------------------- @@ -209,7 +209,7 @@ LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:91:13 + --> $DIR/unnecessary_lazy_eval.rs:106:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^-------------------------------------- @@ -217,7 +217,7 @@ LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:113:35 + --> $DIR/unnecessary_lazy_eval.rs:128:35 | LL | let _: Result = res.and_then(|_| Err(2)); | ^^^^-------------------- @@ -225,7 +225,7 @@ LL | let _: Result = res.and_then(|_| Err(2)); | help: use `and(..)` instead: `and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:114:35 + --> $DIR/unnecessary_lazy_eval.rs:129:35 | LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | ^^^^--------------------------------- @@ -233,7 +233,7 @@ LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | help: use `and(..)` instead: `and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:115:35 + --> $DIR/unnecessary_lazy_eval.rs:130:35 | LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); | ^^^^------------------------------------- @@ -241,7 +241,7 @@ LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)) | help: use `and(..)` instead: `and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:117:35 + --> $DIR/unnecessary_lazy_eval.rs:132:35 | LL | let _: Result = res.or_else(|_| Ok(2)); | ^^^^------------------ @@ -249,7 +249,7 @@ LL | let _: Result = res.or_else(|_| Ok(2)); | help: use `or(..)` instead: `or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:118:35 + --> $DIR/unnecessary_lazy_eval.rs:133:35 | LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | ^^^^------------------------------- @@ -257,7 +257,7 @@ LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | help: use `or(..)` instead: `or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:119:35 + --> $DIR/unnecessary_lazy_eval.rs:134:35 | LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | ^^^^----------------------------------- @@ -265,7 +265,7 @@ LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:120:35 + --> $DIR/unnecessary_lazy_eval.rs:135:35 | LL | let _: Result = res. | ___________________________________^ diff --git a/tests/ui/upper_case_acronyms.rs b/tests/ui/upper_case_acronyms.rs index 48bb9e54b122..9b7c2f28e1cf 100644 --- a/tests/ui/upper_case_acronyms.rs +++ b/tests/ui/upper_case_acronyms.rs @@ -38,4 +38,13 @@ enum ParseErrorPrivate { Parse(T, String), } +// do lint here +struct JSON; + +// do lint here +enum YAML { + Num(u32), + Str(String), +} + fn main() {} diff --git a/tests/ui/upper_case_acronyms.stderr b/tests/ui/upper_case_acronyms.stderr index 250b196a99eb..74082ec16dd4 100644 --- a/tests/ui/upper_case_acronyms.stderr +++ b/tests/ui/upper_case_acronyms.stderr @@ -54,5 +54,17 @@ error: name `WASD` contains a capitalized acronym LL | WASD(u8), | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd` -error: aborting due to 9 previous errors +error: name `JSON` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:42:8 + | +LL | struct JSON; + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Json` + +error: name `YAML` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:45:6 + | +LL | enum YAML { + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Yaml` + +error: aborting due to 11 previous errors diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs index 322083511ac1..8c29e15b1459 100644 --- a/tests/ui/used_underscore_binding.rs +++ b/tests/ui/used_underscore_binding.rs @@ -1,9 +1,8 @@ // aux-build:proc_macro_derive.rs - #![feature(rustc_private)] #![warn(clippy::all)] -#![allow(clippy::disallowed_names, clippy::eq_op)] #![warn(clippy::used_underscore_binding)] +#![allow(clippy::disallowed_names, clippy::eq_op, clippy::uninlined_format_args)] #[macro_use] extern crate proc_macro_derive; diff --git a/tests/ui/used_underscore_binding.stderr b/tests/ui/used_underscore_binding.stderr index 61a9161d212d..875fafe438a1 100644 --- a/tests/ui/used_underscore_binding.stderr +++ b/tests/ui/used_underscore_binding.stderr @@ -1,5 +1,5 @@ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:25:5 + --> $DIR/used_underscore_binding.rs:24:5 | LL | _foo + 1 | ^^^^ @@ -7,31 +7,31 @@ LL | _foo + 1 = note: `-D clippy::used-underscore-binding` implied by `-D warnings` error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:30:20 + --> $DIR/used_underscore_binding.rs:29:20 | LL | println!("{}", _foo); | ^^^^ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:31:16 + --> $DIR/used_underscore_binding.rs:30:16 | LL | assert_eq!(_foo, _foo); | ^^^^ error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:31:22 + --> $DIR/used_underscore_binding.rs:30:22 | LL | assert_eq!(_foo, _foo); | ^^^^ error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:44:5 + --> $DIR/used_underscore_binding.rs:43:5 | LL | s._underscore_field += 1; | ^^^^^^^^^^^^^^^^^^^ error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used - --> $DIR/used_underscore_binding.rs:105:16 + --> $DIR/used_underscore_binding.rs:104:16 | LL | uses_i(_i); | ^^ diff --git a/tests/ui/useless_asref.fixed b/tests/ui/useless_asref.fixed index 90cb8945e77f..38e4b9201e6d 100644 --- a/tests/ui/useless_asref.fixed +++ b/tests/ui/useless_asref.fixed @@ -1,7 +1,6 @@ // run-rustfix - #![deny(clippy::useless_asref)] -#![allow(clippy::explicit_auto_deref)] +#![allow(clippy::explicit_auto_deref, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/useless_asref.rs b/tests/ui/useless_asref.rs index cb9f8ae5909a..f1e83f9d396c 100644 --- a/tests/ui/useless_asref.rs +++ b/tests/ui/useless_asref.rs @@ -1,7 +1,6 @@ // run-rustfix - #![deny(clippy::useless_asref)] -#![allow(clippy::explicit_auto_deref)] +#![allow(clippy::explicit_auto_deref, clippy::uninlined_format_args)] use std::fmt::Debug; diff --git a/tests/ui/useless_asref.stderr b/tests/ui/useless_asref.stderr index b21c67bb3645..67ce8b64e0e3 100644 --- a/tests/ui/useless_asref.stderr +++ b/tests/ui/useless_asref.stderr @@ -1,71 +1,71 @@ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:44:18 + --> $DIR/useless_asref.rs:43:18 | LL | foo_rstr(rstr.as_ref()); | ^^^^^^^^^^^^^ help: try this: `rstr` | note: the lint level is defined here - --> $DIR/useless_asref.rs:3:9 + --> $DIR/useless_asref.rs:2:9 | LL | #![deny(clippy::useless_asref)] | ^^^^^^^^^^^^^^^^^^^^^ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:46:20 + --> $DIR/useless_asref.rs:45:20 | LL | foo_rslice(rslice.as_ref()); | ^^^^^^^^^^^^^^^ help: try this: `rslice` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:50:21 + --> $DIR/useless_asref.rs:49:21 | LL | foo_mrslice(mrslice.as_mut()); | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:52:20 + --> $DIR/useless_asref.rs:51:20 | LL | foo_rslice(mrslice.as_ref()); | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:59:20 + --> $DIR/useless_asref.rs:58:20 | LL | foo_rslice(rrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:61:18 + --> $DIR/useless_asref.rs:60:18 | LL | foo_rstr(rrrrrstr.as_ref()); | ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:66:21 + --> $DIR/useless_asref.rs:65:21 | LL | foo_mrslice(mrrrrrslice.as_mut()); | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:68:20 + --> $DIR/useless_asref.rs:67:20 | LL | foo_rslice(mrrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:72:16 + --> $DIR/useless_asref.rs:71:16 | LL | foo_rrrrmr((&&&&MoreRef).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:122:13 + --> $DIR/useless_asref.rs:121:13 | LL | foo_mrt(mrt.as_mut()); | ^^^^^^^^^^^^ help: try this: `mrt` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:124:12 + --> $DIR/useless_asref.rs:123:12 | LL | foo_rt(mrt.as_ref()); | ^^^^^^^^^^^^ help: try this: `mrt` diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index 318f9c2dceb6..2518d8049150 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::nonstandard_macro_braces)] #![warn(clippy::useless_vec)] +#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args)] #[derive(Debug)] struct NonCopy; diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index d7673ce3e643..e1492e2f3aef 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::nonstandard_macro_braces)] #![warn(clippy::useless_vec)] +#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args)] #[derive(Debug)] struct NonCopy; diff --git a/tests/ui/while_let_loop.rs b/tests/ui/while_let_loop.rs index c42e2a79a9bf..5b8075731cb7 100644 --- a/tests/ui/while_let_loop.rs +++ b/tests/ui/while_let_loop.rs @@ -1,4 +1,5 @@ #![warn(clippy::while_let_loop)] +#![allow(clippy::uninlined_format_args)] fn main() { let y = Some(true); diff --git a/tests/ui/while_let_loop.stderr b/tests/ui/while_let_loop.stderr index 13dd0ee224c1..04808c0b3ada 100644 --- a/tests/ui/while_let_loop.stderr +++ b/tests/ui/while_let_loop.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:5:5 + --> $DIR/while_let_loop.rs:6:5 | LL | / loop { LL | | if let Some(_x) = y { @@ -13,7 +13,7 @@ LL | | } = note: `-D clippy::while-let-loop` implied by `-D warnings` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:22:5 + --> $DIR/while_let_loop.rs:23:5 | LL | / loop { LL | | match y { @@ -24,7 +24,7 @@ LL | | } | |_____^ help: try: `while let Some(_x) = y { .. }` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:29:5 + --> $DIR/while_let_loop.rs:30:5 | LL | / loop { LL | | let x = match y { @@ -36,7 +36,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:38:5 + --> $DIR/while_let_loop.rs:39:5 | LL | / loop { LL | | let x = match y { @@ -48,7 +48,7 @@ LL | | } | |_____^ help: try: `while let Some(x) = y { .. }` error: this loop could be written as a `while let` loop - --> $DIR/while_let_loop.rs:68:5 + --> $DIR/while_let_loop.rs:69:5 | LL | / loop { LL | | let (e, l) = match "".split_whitespace().next() { diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index c57c46736342..5afa0a89f82c 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -1,14 +1,12 @@ // run-rustfix - #![warn(clippy::while_let_on_iterator)] +#![allow(dead_code, unreachable_code, unused_mut)] #![allow( - clippy::never_loop, - unreachable_code, - unused_mut, - dead_code, clippy::equatable_if_let, clippy::manual_find, - clippy::redundant_closure_call + clippy::never_loop, + clippy::redundant_closure_call, + clippy::uninlined_format_args )] fn base() { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 8b9a2dbcce3a..3de586c9d8fd 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -1,14 +1,12 @@ // run-rustfix - #![warn(clippy::while_let_on_iterator)] +#![allow(dead_code, unreachable_code, unused_mut)] #![allow( - clippy::never_loop, - unreachable_code, - unused_mut, - dead_code, clippy::equatable_if_let, clippy::manual_find, - clippy::redundant_closure_call + clippy::never_loop, + clippy::redundant_closure_call, + clippy::uninlined_format_args )] fn base() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 3236765e1db0..4d98666190d6 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -1,5 +1,5 @@ error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:16:5 + --> $DIR/while_let_on_iterator.rs:14:5 | LL | while let Option::Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` @@ -7,151 +7,151 @@ LL | while let Option::Some(x) = iter.next() { = note: `-D clippy::while-let-on-iterator` implied by `-D warnings` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:21:5 + --> $DIR/while_let_on_iterator.rs:19:5 | LL | while let Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:26:5 + --> $DIR/while_let_on_iterator.rs:24:5 | LL | while let Some(_) = iter.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:102:9 + --> $DIR/while_let_on_iterator.rs:100:9 | LL | while let Some([..]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:109:9 + --> $DIR/while_let_on_iterator.rs:107:9 | LL | while let Some([_x]) = it.next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:122:9 + --> $DIR/while_let_on_iterator.rs:120:9 | LL | while let Some(x @ [_]) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:142:9 + --> $DIR/while_let_on_iterator.rs:140:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:199:9 + --> $DIR/while_let_on_iterator.rs:197:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:210:5 + --> $DIR/while_let_on_iterator.rs:208:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:212:9 + --> $DIR/while_let_on_iterator.rs:210:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:221:9 + --> $DIR/while_let_on_iterator.rs:219:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:230:9 + --> $DIR/while_let_on_iterator.rs:228:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:247:9 + --> $DIR/while_let_on_iterator.rs:245:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:262:13 + --> $DIR/while_let_on_iterator.rs:260:13 | LL | while let Some(i) = self.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:294:13 + --> $DIR/while_let_on_iterator.rs:292:13 | LL | while let Some(i) = self.0.0.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:323:5 + --> $DIR/while_let_on_iterator.rs:321:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:335:9 + --> $DIR/while_let_on_iterator.rs:333:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:349:5 + --> $DIR/while_let_on_iterator.rs:347:5 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:360:5 + --> $DIR/while_let_on_iterator.rs:358:5 | LL | while let Some(x) = it.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:395:5 + --> $DIR/while_let_on_iterator.rs:393:5 | LL | while let Some(x) = s.x.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:402:5 + --> $DIR/while_let_on_iterator.rs:400:5 | LL | while let Some(x) = x[0].next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:410:9 + --> $DIR/while_let_on_iterator.rs:408:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:420:9 + --> $DIR/while_let_on_iterator.rs:418:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:430:9 + --> $DIR/while_let_on_iterator.rs:428:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:440:9 + --> $DIR/while_let_on_iterator.rs:438:9 | LL | while let Some(x) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:450:5 + --> $DIR/while_let_on_iterator.rs:448:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index 3ee4ab48ac84..23607497841e 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -1,15 +1,13 @@ // run-rustfix // aux-build:non-exhaustive-enum.rs - #![deny(clippy::wildcard_enum_match_arm)] +#![allow(dead_code, unreachable_code, unused_variables)] #![allow( - unreachable_code, - unused_variables, - dead_code, + clippy::diverging_sub_expression, clippy::single_match, - clippy::wildcard_in_or_patterns, + clippy::uninlined_format_args, clippy::unnested_or_patterns, - clippy::diverging_sub_expression + clippy::wildcard_in_or_patterns )] extern crate non_exhaustive_enum; diff --git a/tests/ui/wildcard_enum_match_arm.rs b/tests/ui/wildcard_enum_match_arm.rs index 468865504533..decd86165f3a 100644 --- a/tests/ui/wildcard_enum_match_arm.rs +++ b/tests/ui/wildcard_enum_match_arm.rs @@ -1,15 +1,13 @@ // run-rustfix // aux-build:non-exhaustive-enum.rs - #![deny(clippy::wildcard_enum_match_arm)] +#![allow(dead_code, unreachable_code, unused_variables)] #![allow( - unreachable_code, - unused_variables, - dead_code, + clippy::diverging_sub_expression, clippy::single_match, - clippy::wildcard_in_or_patterns, + clippy::uninlined_format_args, clippy::unnested_or_patterns, - clippy::diverging_sub_expression + clippy::wildcard_in_or_patterns )] extern crate non_exhaustive_enum; diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index d63f20903531..efecc9576cc7 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -1,41 +1,41 @@ error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:42:9 + --> $DIR/wildcard_enum_match_arm.rs:40:9 | LL | _ => eprintln!("Not red"), | ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` | note: the lint level is defined here - --> $DIR/wildcard_enum_match_arm.rs:4:9 + --> $DIR/wildcard_enum_match_arm.rs:3:9 | LL | #![deny(clippy::wildcard_enum_match_arm)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:46:9 + --> $DIR/wildcard_enum_match_arm.rs:44:9 | LL | _not_red => eprintln!("Not red"), | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:50:9 + --> $DIR/wildcard_enum_match_arm.rs:48:9 | LL | not_red => format!("{:?}", not_red), | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:66:9 + --> $DIR/wildcard_enum_match_arm.rs:64:9 | LL | _ => "No red", | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` error: wildcard matches known variants and will also match future added variants - --> $DIR/wildcard_enum_match_arm.rs:83:9 + --> $DIR/wildcard_enum_match_arm.rs:81:9 | LL | _ => {}, | ^ help: try this: `ErrorKind::PermissionDenied | _` error: wildcard matches known variants and will also match future added variants - --> $DIR/wildcard_enum_match_arm.rs:101:13 + --> $DIR/wildcard_enum_match_arm.rs:99:13 | LL | _ => (), | ^ help: try this: `Enum::B | _` diff --git a/tests/ui/write_literal.rs b/tests/ui/write_literal.rs index 5892818aa9a6..218385ea1296 100644 --- a/tests/ui/write_literal.rs +++ b/tests/ui/write_literal.rs @@ -1,5 +1,5 @@ -#![allow(unused_must_use)] #![warn(clippy::write_literal)] +#![allow(clippy::uninlined_format_args, unused_must_use)] use std::io::Write; diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 38498ebdcf2c..9e07769a8e4f 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -8,12 +8,12 @@ use std::fs; #[test] fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { fn read_version(path: &str) -> String { - let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{}`: {:?}", path, e)); + let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{path}`: {e:?}")); contents .lines() .filter_map(|l| l.split_once('=')) .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim())) - .unwrap_or_else(|| panic!("error finding version in `{}`", path)) + .unwrap_or_else(|| panic!("error finding version in `{path}`")) .to_string() } @@ -83,7 +83,7 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { // we don't want our tests failing suddenly }, _ => { - panic!("Failed to parse rustc version: {:?}", vsplit); + panic!("Failed to parse rustc version: {vsplit:?}"); }, }; } From 13dbc33d8f129453212116b90cbd96b0a013d23a Mon Sep 17 00:00:00 2001 From: ouz-a Date: Tue, 4 Oct 2022 21:39:43 +0300 Subject: [PATCH 006/524] Remove `mir::CastKind::Misc` --- clippy_utils/src/qualify_min_const_fn.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index f7ce71917726..7af27ebb9883 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -129,7 +129,12 @@ fn check_rvalue<'tcx>( | Rvalue::Use(operand) | Rvalue::Cast( CastKind::PointerFromExposedAddress - | CastKind::Misc + | CastKind::IntToInt + | CastKind::FloatToInt + | CastKind::IntToFloat + | CastKind::FloatToFloat + | CastKind::FnPtrToPtr + | CastKind::PtrToPtr | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _, From 09a554db25840a35bbfc13d531e288801b10fe6a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 6 Oct 2022 17:41:53 +0200 Subject: [PATCH 007/524] Merge commit '8f1ebdd18bdecc621f16baaf779898cc08cc2766' into clippyup --- clippy_lints/src/attrs.rs | 3 +- clippy_lints/src/format_args.rs | 17 +++------ clippy_utils/src/macros.rs | 2 +- tests/ui/uninlined_format_args.fixed | 11 ++++-- tests/ui/uninlined_format_args.stderr | 53 +-------------------------- tests/ui/unsafe_removed_from_name.rs | 3 ++ 6 files changed, 20 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 5f45c69d7f98..0bd1f8b784e8 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -357,7 +357,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate" - | "macro_use_imports", + | "macro_use_imports" + | "unsafe_removed_from_name", ) }) { diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index cefebc2a98a2..99bef62f8143 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -8,7 +8,7 @@ use if_chain::if_chain; use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; use rustc_semver::RustcVersion; @@ -173,17 +173,10 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si return; } - // FIXME: Properly ignore a rare case where the format string is wrapped in a macro. - // Example: `format!(indoc!("{}"), foo);` - // If inlined, they will cause a compilation error: - // > to avoid ambiguity, `format_args!` cannot capture variables - // > when the format string is expanded from a macro - // @Alexendoo explanation: - // > indoc! is a proc macro that is producing a string literal with its span - // > set to its input it's not marked as from expansion, and since it's compatible - // > tokenization wise clippy_utils::is_from_proc_macro wouldn't catch it either - // This might be a relatively expensive test, so do it only we are ready to replace. - // See more examples in tests/ui/uninlined_format_args.rs + // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 + if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) { + return; + } span_lint_and_then( cx, diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index dd0ce1da6575..5a63c290a315 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -414,7 +414,7 @@ impl FormatString { struct FormatArgsValues<'tcx> { /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for - /// `format!("{x} {} {y}", 1, z + 2)`. + /// `format!("{x} {} {}", 1, z + 2)`. value_args: Vec<&'tcx Expr<'tcx>>, /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in /// `value_args` diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index dcf10ed60a25..3ca7a4019025 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -44,7 +44,9 @@ fn tester(fn_arg: i32) { println!("val='{local_i32}'"); // space+tab println!("val='{local_i32}'"); // tab+space println!( - "val='{local_i32}'" + "val='{ + }'", + local_i32 ); println!("{local_i32}"); println!("{fn_arg}"); @@ -108,7 +110,8 @@ fn tester(fn_arg: i32) { println!("{local_f64:width$.prec$}"); println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); println!( - "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", + local_i32, width, prec, ); println!( "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", @@ -139,7 +142,9 @@ fn tester(fn_arg: i32) { println!(no_param_str!(), local_i32); println!( - "{val}", + "{}", + // comment with a comma , in it + val, ); println!("{val}"); diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index 1b4dada28dac..d1a774926342 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -59,22 +59,6 @@ LL - println!("val='{ }'", local_i32); // tab+space LL + println!("val='{local_i32}'"); // tab+space | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:46:5 - | -LL | / println!( -LL | | "val='{ -LL | | }'", -LL | | local_i32 -LL | | ); - | |_____^ - | -help: change this to - | -LL - "val='{ -LL + "val='{local_i32}'" - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:51:5 | @@ -783,25 +767,6 @@ LL - println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:112:5 - | -LL | / println!( -LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", -LL | | local_i32, width, prec, -LL | | ); - | |_____^ - | -help: change this to - | -LL ~ "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", width, prec, -LL ~ "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:123:5 | @@ -850,22 +815,6 @@ LL - println!("{}", format!("{}", local_i32)); LL + println!("{}", format!("{local_i32}")); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:144:5 - | -LL | / println!( -LL | | "{}", -LL | | // comment with a comma , in it -LL | | val, -LL | | ); - | |_____^ - | -help: change this to - | -LL - "{}", -LL + "{val}", - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:149:5 | @@ -890,5 +839,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 73 previous errors +error: aborting due to 70 previous errors diff --git a/tests/ui/unsafe_removed_from_name.rs b/tests/ui/unsafe_removed_from_name.rs index cde4e96d668c..d29888ac62f6 100644 --- a/tests/ui/unsafe_removed_from_name.rs +++ b/tests/ui/unsafe_removed_from_name.rs @@ -24,4 +24,7 @@ use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; use mod_with_some_unsafe_things::Safe as IPromiseItsSafeThisTime; use mod_with_some_unsafe_things::Unsafe as SuperUnsafeModThing; +#[allow(clippy::unsafe_removed_from_name)] +use mod_with_some_unsafe_things::Unsafe as SuperSafeThing; + fn main() {} From 3b328e704953e34de401166cd76ca062e21d83d8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 9 Sep 2022 15:08:06 -0500 Subject: [PATCH 008/524] Introduce TypeErrCtxt TypeErrCtxt optionally has a TypeckResults so that InferCtxt doesn't need to. --- clippy_lints/src/future_not_send.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index eb2eefe0d5a1..406c842a6d05 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{EarlyBinder, Opaque, PredicateKind::Trait}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; -use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use rustc_trait_selection::traits::{self, FulfillmentError}; declare_clippy_lint! { @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { |db| { cx.tcx.infer_ctxt().enter(|infcx| { for FulfillmentError { obligation, .. } in send_errors { - infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); + infcx.err_ctxt().maybe_note_obligation_cause_for_async_await(db, &obligation); if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { db.note(&format!( "`{}` doesn't implement `{}`", From 6819e85501348c6fab3c5d40d0e31d5c7d00bb6a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 19 Sep 2022 22:03:59 -0500 Subject: [PATCH 009/524] Change InferCtxtBuilder from enter to build --- clippy_lints/src/dereference.rs | 14 +++--- clippy_lints/src/escape.rs | 5 +- clippy_lints/src/future_not_send.rs | 29 ++++++----- clippy_lints/src/loops/mut_range_bound.rs | 19 ++++---- .../src/methods/unnecessary_to_owned.rs | 4 +- clippy_lints/src/needless_pass_by_value.rs | 6 +-- .../src/operators/assign_op_pattern.rs | 38 +++++++-------- clippy_utils/src/sugg.rs | 7 ++- clippy_utils/src/ty.rs | 48 +++++++++---------- clippy_utils/src/usage.rs | 19 ++++---- 10 files changed, 87 insertions(+), 102 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 3cd8f236e7a5..02a16f765b73 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -831,11 +831,10 @@ fn walk_parents<'tcx>( // Trait methods taking `self` arg_ty } && impl_ty.is_ref() - && cx.tcx.infer_ctxt().enter(|infcx| - infcx - .type_implements_trait(trait_id, impl_ty, subs, cx.param_env) - .must_apply_modulo_regions() - ) + && let infcx = cx.tcx.infer_ctxt().build() + && infcx + .type_implements_trait(trait_id, impl_ty, subs, cx.param_env) + .must_apply_modulo_regions() { return Some(Position::MethodReceiverRefImpl) } @@ -1119,9 +1118,8 @@ fn needless_borrow_impl_arg_position<'tcx>( let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty); let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); - cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) + let infcx = cx.tcx.infer_ctxt().build(); + infcx.predicate_must_hold_modulo_regions(&obligation) }) }; diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 2e608fe527fd..eb0455ae404c 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -106,9 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { }; let fn_def_id = cx.tcx.hir().local_def_id(hir_id); - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); for node in v.set { span_lint_hir( diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 406c842a6d05..0519f9ac2468 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -77,10 +77,9 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { if is_future { let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap(); let span = decl.output.span(); - let send_errors = cx.tcx.infer_ctxt().enter(|infcx| { - let cause = traits::ObligationCause::misc(span, hir_id); - traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait) - }); + let infcx = cx.tcx.infer_ctxt().build(); + let cause = traits::ObligationCause::misc(span, hir_id); + let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait); if !send_errors.is_empty() { span_lint_and_then( cx, @@ -88,18 +87,18 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { span, "future cannot be sent between threads safely", |db| { - cx.tcx.infer_ctxt().enter(|infcx| { - for FulfillmentError { obligation, .. } in send_errors { - infcx.err_ctxt().maybe_note_obligation_cause_for_async_await(db, &obligation); - if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { - db.note(&format!( - "`{}` doesn't implement `{}`", - trait_pred.self_ty(), - trait_pred.trait_ref.print_only_trait_path(), - )); - } + for FulfillmentError { obligation, .. } in send_errors { + infcx + .err_ctxt() + .maybe_note_obligation_cause_for_async_await(db, &obligation); + if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { + db.note(&format!( + "`{}` doesn't implement `{}`", + trait_pred.self_ty(), + trait_pred.trait_ref.print_only_trait_path(), + )); } - }); + } }, ); } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 0ee42b61c9a5..db73ab55b37c 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -65,16 +65,15 @@ fn check_for_mutation<'tcx>( span_low: None, span_high: None, }; - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - body.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new( + &mut delegate, + &infcx, + body.hir_id.owner.def_id, + cx.param_env, + cx.typeck_results(), + ) + .walk_expr(body); delegate.mutation_span() } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 9ab0d6141146..6017941452c0 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -420,9 +420,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< if trait_predicates.any(|predicate| { let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst); let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); - !cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) + !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) }) { return false; } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 178c973981b1..7f881e27dd27 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -138,10 +138,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { .. } = { let mut ctx = MovedVariablesCtxt::default(); - cx.tcx.infer_ctxt().enter(|infcx| { - euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) - .consume_body(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); ctx }; diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 26bca7c306a8..c7e964cf23e2 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -123,16 +123,15 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet } let mut s = S(hir::HirIdSet::default()); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); - v.consume_expr(e); - }); + let infcx = cx.tcx.infer_ctxt().build(); + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); s.0 } @@ -156,15 +155,14 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet } let mut s = S(hir::HirIdSet::default()); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); - v.consume_expr(e); - }); + let infcx = cx.tcx.infer_ctxt().build(); + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); s.0 } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index ef836e84829b..3c5dd92b9cd6 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -821,10 +821,9 @@ pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<' }; let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id); - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) - .consume_body(closure_body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) + .consume_body(closure_body); if !visitor.suggestion_start.is_empty() { return Some(DerefClosure { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 934470bd135b..a15daec7c3ce 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -172,11 +172,10 @@ pub fn implements_trait_with_env<'tcx>( return false; } let ty_params = tcx.mk_substs(ty_params.iter()); - tcx.infer_ctxt().enter(|infcx| { - infcx - .type_implements_trait(trait_id, ty, ty_params, param_env) - .must_apply_modulo_regions() - }) + let infcx = tcx.infer_ctxt().build(); + infcx + .type_implements_trait(trait_id, ty, ty_params, param_env) + .must_apply_modulo_regions() } /// Checks whether this type implements `Drop`. @@ -242,27 +241,26 @@ fn is_normalizable_helper<'tcx>( } // prevent recursive loops, false-negative is better than endless loop leading to stack overflow cache.insert(ty, false); - let result = cx.tcx.infer_ctxt().enter(|infcx| { - let cause = rustc_middle::traits::ObligationCause::dummy(); - if infcx.at(&cause, param_env).normalize(ty).is_ok() { - match ty.kind() { - ty::Adt(def, substs) => def.variants().iter().all(|variant| { - variant - .fields - .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) - }), - _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { - GenericArgKind::Type(inner_ty) if inner_ty != ty => { - is_normalizable_helper(cx, param_env, inner_ty, cache) - }, - _ => true, // if inner_ty == ty, we've already checked it - }), - } - } else { - false + let infcx = cx.tcx.infer_ctxt().build(); + let cause = rustc_middle::traits::ObligationCause::dummy(); + let result = if infcx.at(&cause, param_env).normalize(ty).is_ok() { + match ty.kind() { + ty::Adt(def, substs) => def.variants().iter().all(|variant| { + variant + .fields + .iter() + .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) + }), + _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { + GenericArgKind::Type(inner_ty) if inner_ty != ty => { + is_normalizable_helper(cx, param_env, inner_ty, cache) + }, + _ => true, // if inner_ty == ty, we've already checked it + }), } - }); + } else { + false + }; cache.insert(ty, result); result } diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index b5ec3fef3e0b..e32bae6ed1fd 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -18,16 +18,15 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> used_mutably: HirIdSet::default(), skip: false, }; - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - expr.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(expr); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new( + &mut delegate, + &infcx, + expr.hir_id.owner.def_id, + cx.param_env, + cx.typeck_results(), + ) + .walk_expr(expr); if delegate.skip { return None; From e91746ed8271850de512fb765a21aa5ddb18a25c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Sep 2022 13:05:20 +0200 Subject: [PATCH 010/524] make const_err a hard error --- clippy_lints/src/indexing_slicing.rs | 1 - tests/ui/crashes/ice-9463.rs | 2 +- tests/ui/crashes/ice-9463.stderr | 2 +- tests/ui/indexing_slicing_index.rs | 2 +- tests/ui/indexing_slicing_index.stderr | 8 +++++++- tests/ui/out_of_bounds_indexing/issue-3102.rs | 2 +- tests/ui/out_of_bounds_indexing/simple.rs | 2 +- 7 files changed, 12 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 4a375752e1d3..af40a5a8187e 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -19,7 +19,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,no_run - /// # #![allow(const_err)] /// let x = [1, 2, 3, 4]; /// /// x[9]; diff --git a/tests/ui/crashes/ice-9463.rs b/tests/ui/crashes/ice-9463.rs index 41ef930d3233..9564e77c24b1 100644 --- a/tests/ui/crashes/ice-9463.rs +++ b/tests/ui/crashes/ice-9463.rs @@ -1,4 +1,4 @@ -#![deny(arithmetic_overflow, const_err)] +#![deny(arithmetic_overflow)] fn main() { let _x = -1_i32 >> -1; let _y = 1u32 >> 10000000000000u32; diff --git a/tests/ui/crashes/ice-9463.stderr b/tests/ui/crashes/ice-9463.stderr index b0ce306d6838..2b425e85a27b 100644 --- a/tests/ui/crashes/ice-9463.stderr +++ b/tests/ui/crashes/ice-9463.stderr @@ -7,7 +7,7 @@ LL | let _x = -1_i32 >> -1; note: the lint level is defined here --> $DIR/ice-9463.rs:1:9 | -LL | #![deny(arithmetic_overflow, const_err)] +LL | #![deny(arithmetic_overflow)] | ^^^^^^^^^^^^^^^^^^^ error: this arithmetic operation will overflow diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 7ebf6ee993cb..4476e0eb9220 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -3,7 +3,7 @@ // We also check the out_of_bounds_indexing lint here, because it lints similar things and // we want to avoid false positives. #![warn(clippy::out_of_bounds_indexing)] -#![allow(const_err, unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] const ARR: [i32; 2] = [1, 2]; const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index a8d8b38163d0..da5bc38b3b66 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -59,6 +59,12 @@ LL | v[M]; | = help: consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 8 previous errors +error[E0080]: evaluation of constant value failed + --> $DIR/indexing_slicing_index.rs:10:24 + | +LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/out_of_bounds_indexing/issue-3102.rs b/tests/ui/out_of_bounds_indexing/issue-3102.rs index f20a0ede1137..edd2123d48a5 100644 --- a/tests/ui/out_of_bounds_indexing/issue-3102.rs +++ b/tests/ui/out_of_bounds_indexing/issue-3102.rs @@ -1,5 +1,5 @@ #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, const_err)] +#![allow(clippy::no_effect)] fn main() { let x = [1, 2, 3, 4]; diff --git a/tests/ui/out_of_bounds_indexing/simple.rs b/tests/ui/out_of_bounds_indexing/simple.rs index 590e578d758e..4c541c23f5f4 100644 --- a/tests/ui/out_of_bounds_indexing/simple.rs +++ b/tests/ui/out_of_bounds_indexing/simple.rs @@ -1,5 +1,5 @@ #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, const_err)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] fn main() { let x = [1, 2, 3, 4]; From 5f6e1d397a940447af5e4279718668db473c683c Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 24 Sep 2022 17:22:04 +0200 Subject: [PATCH 011/524] Stabilize half_open_range_patterns --- tests/ui/match_overlapping_arm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 2f85e6357135..22b04b208f87 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -1,5 +1,5 @@ #![feature(exclusive_range_pattern)] -#![feature(half_open_range_patterns)] + #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let)] From 6f4546a4be7b65da15b3170d72d3339ed1d2aeec Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 8 Oct 2022 16:15:18 +0200 Subject: [PATCH 012/524] [`unnecessary_cast`] Do not lint negative hexadecimal literals when cast as float Floats cannot be expressed as hexadecimal literals --- clippy_lints/src/casts/unnecessary_cast.rs | 3 --- clippy_utils/src/numeric_literal.rs | 7 ++++--- tests/ui/unnecessary_cast.fixed | 4 ++++ tests/ui/unnecessary_cast.rs | 4 ++++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 21ed7f4844cc..c8596987e4d7 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -59,9 +59,6 @@ pub(super) fn check<'tcx>( lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); return false; }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => { - return false; - }, LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 80098d9766c6..c5dcd7b31f58 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -69,12 +69,13 @@ impl<'a> NumericLiteral<'a> { #[must_use] pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self { + let unsigned_lit = lit.trim_start_matches('-'); // Determine delimiter for radix prefix, if present, and radix. - let radix = if lit.starts_with("0x") { + let radix = if unsigned_lit.starts_with("0x") { Radix::Hexadecimal - } else if lit.starts_with("0b") { + } else if unsigned_lit.starts_with("0b") { Radix::Binary - } else if lit.starts_with("0o") { + } else if unsigned_lit.starts_with("0o") { Radix::Octal } else { Radix::Decimal diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 94dc96427263..ec8c6abfab91 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -111,4 +111,8 @@ mod fixable { let _num = foo(); } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index e5150256f69a..5213cdc269bd 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -111,4 +111,8 @@ mod fixable { let _num = foo() as f32; } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } From 8e76d6687e48d9cfaa28553278540682cca6602c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 9 Oct 2022 07:09:57 +0000 Subject: [PATCH 013/524] ImplItemKind::TyAlias => ImplItemKind::Type --- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/types/mod.rs | 2 +- clippy_utils/src/check_proc_macro.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 9d5764ac0926..ef6d1da552bf 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -148,7 +148,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let desc = match impl_item.kind { hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) => return, + hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) => return, }; let assoc_item = cx.tcx.associated_item(impl_item.def_id); diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index aca55817c525..33eee2a03784 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -372,7 +372,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { // Methods are covered by check_fn. // Type aliases are ignored because oftentimes it's impossible to // make type alias declaration in trait simpler, see #1013 - ImplItemKind::Fn(..) | ImplItemKind::TyAlias(..) => (), + ImplItemKind::Fn(..) | ImplItemKind::Type(..) => (), } } diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 7a8d4e8068ed..c6bf98b7b8bb 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -220,7 +220,7 @@ fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) { fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) { let (start_pat, end_pat) = match &item.kind { ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")), - ImplItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")), + ImplItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")), ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")), }; if item.vis_span.is_empty() { From 3fc903eb9578287f8e619b597e98b26e7a27b000 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 25 Aug 2022 14:03:13 +0400 Subject: [PATCH 014/524] Fix clippy tests that trigger `for_loop_over_fallibles` lint --- tests/ui/for_loop_unfixable.rs | 1 + tests/ui/for_loop_unfixable.stderr | 2 +- tests/ui/for_loops_over_fallibles.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index efcaffce24ea..203656fa4d6c 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -8,6 +8,7 @@ clippy::for_kv_map )] #[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] +#[allow(for_loop_over_fallibles)] fn main() { let vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loop_unfixable.stderr b/tests/ui/for_loop_unfixable.stderr index f769b4bdc941..50a86eaa68f7 100644 --- a/tests/ui/for_loop_unfixable.stderr +++ b/tests/ui/for_loop_unfixable.stderr @@ -1,5 +1,5 @@ error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loop_unfixable.rs:14:15 + --> $DIR/for_loop_unfixable.rs:15:15 | LL | for _v in vec.iter().next() {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 4b2a9297d084..54661ff94f24 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -1,5 +1,6 @@ #![warn(clippy::for_loops_over_fallibles)] #![allow(clippy::uninlined_format_args)] +#![allow(for_loop_over_fallibles)] fn for_loops_over_fallibles() { let option = Some(1); From 05dcfd971a2736cf0eac04bb04f6539f36d05a44 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 15:59:39 +0000 Subject: [PATCH 015/524] fixup lint name --- tests/ui/for_loop_unfixable.rs | 2 +- tests/ui/for_loops_over_fallibles.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index 203656fa4d6c..55fb3788a8b1 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -8,7 +8,7 @@ clippy::for_kv_map )] #[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] -#[allow(for_loop_over_fallibles)] +#[allow(for_loops_over_fallibles)] fn main() { let vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 54661ff94f24..75cdcc02353f 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -1,6 +1,6 @@ #![warn(clippy::for_loops_over_fallibles)] #![allow(clippy::uninlined_format_args)] -#![allow(for_loop_over_fallibles)] +#![allow(for_loops_over_fallibles)] fn for_loops_over_fallibles() { let option = Some(1); From 7cfc6fa1f0c847c749929925f9def0fdc690d417 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 7 Oct 2022 17:08:29 +0000 Subject: [PATCH 016/524] deprecate `clippy::for_loops_over_fallibles` --- clippy_lints/src/lib.register_all.rs | 1 - clippy_lints/src/lib.register_lints.rs | 1 - clippy_lints/src/lib.register_suspicious.rs | 1 - .../src/loops/for_loops_over_fallibles.rs | 65 ------------- clippy_lints/src/loops/iter_next_loop.rs | 5 +- clippy_lints/src/loops/mod.rs | 57 +---------- clippy_lints/src/renamed_lints.rs | 5 +- src/docs.rs | 1 - src/docs/for_loops_over_fallibles.txt | 32 ------- tests/ui/for_loops_over_fallibles.rs | 74 --------------- tests/ui/for_loops_over_fallibles.stderr | 95 ------------------- tests/ui/manual_map_option.fixed | 2 +- tests/ui/manual_map_option.rs | 2 +- tests/ui/rename.fixed | 7 +- tests/ui/rename.rs | 3 +- tests/ui/rename.stderr | 34 ++++--- 16 files changed, 34 insertions(+), 351 deletions(-) delete mode 100644 clippy_lints/src/loops/for_loops_over_fallibles.rs delete mode 100644 src/docs/for_loops_over_fallibles.txt delete mode 100644 tests/ui/for_loops_over_fallibles.rs delete mode 100644 tests/ui/for_loops_over_fallibles.stderr diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 5d26e4b33601..fe1f0b56646c 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -109,7 +109,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(loops::EMPTY_LOOP), LintId::of(loops::EXPLICIT_COUNTER_LOOP), LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::MANUAL_FIND), LintId::of(loops::MANUAL_FLATTEN), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 05d927dbea79..306cb6a61c94 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -227,7 +227,6 @@ store.register_lints(&[ loops::EXPLICIT_INTO_ITER_LOOP, loops::EXPLICIT_ITER_LOOP, loops::FOR_KV_MAP, - loops::FOR_LOOPS_OVER_FALLIBLES, loops::ITER_NEXT_LOOP, loops::MANUAL_FIND, loops::MANUAL_FLATTEN, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 6125d0f7a862..d6d95c95c85d 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -21,7 +21,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::MUT_RANGE_BOUND), LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::SUSPICIOUS_MAP), diff --git a/clippy_lints/src/loops/for_loops_over_fallibles.rs b/clippy_lints/src/loops/for_loops_over_fallibles.rs deleted file mode 100644 index 77de90fd7b94..000000000000 --- a/clippy_lints/src/loops/for_loops_over_fallibles.rs +++ /dev/null @@ -1,65 +0,0 @@ -use super::FOR_LOOPS_OVER_FALLIBLES; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use rustc_hir::{Expr, Pat}; -use rustc_lint::LateContext; -use rustc_span::symbol::sym; - -/// Checks for `for` loops over `Option`s and `Result`s. -pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, method_name: Option<&str>) { - let ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, ty, sym::Option) { - let help_string = if let Some(method_name) = method_name { - format!( - "consider replacing `for {0} in {1}.{method_name}()` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - } else { - format!( - "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - }; - span_lint_and_help( - cx, - FOR_LOOPS_OVER_FALLIBLES, - arg.span, - &format!( - "for loop over `{0}`, which is an `Option`. This is more readably written as an \ - `if let` statement", - snippet(cx, arg.span, "_") - ), - None, - &help_string, - ); - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - let help_string = if let Some(method_name) = method_name { - format!( - "consider replacing `for {0} in {1}.{method_name}()` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - } else { - format!( - "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - }; - span_lint_and_help( - cx, - FOR_LOOPS_OVER_FALLIBLES, - arg.span, - &format!( - "for loop over `{0}`, which is a `Result`. This is more readably written as an \ - `if let` statement", - snippet(cx, arg.span, "_") - ), - None, - &help_string, - ); - } -} diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index e640c62ebdac..b8a263817d29 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) { if is_trait_method(cx, arg, sym::Iterator) { span_lint( cx, @@ -14,8 +14,5 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ probably not what you want", ); - true - } else { - false } } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index c0a0444485e3..bcf278d9c833 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -3,7 +3,6 @@ mod explicit_counter_loop; mod explicit_into_iter_loop; mod explicit_iter_loop; mod for_kv_map; -mod for_loops_over_fallibles; mod iter_next_loop; mod manual_find; mod manual_flatten; @@ -173,49 +172,6 @@ declare_clippy_lint! { "for-looping over `_.next()` which is probably not intended" } -declare_clippy_lint! { - /// ### What it does - /// Checks for `for` loops over `Option` or `Result` values. - /// - /// ### Why is this bad? - /// Readability. This is more clearly expressed as an `if - /// let`. - /// - /// ### Example - /// ```rust - /// # let opt = Some(1); - /// # let res: Result = Ok(1); - /// for x in opt { - /// // .. - /// } - /// - /// for x in &res { - /// // .. - /// } - /// - /// for x in res.iter() { - /// // .. - /// } - /// ``` - /// - /// Use instead: - /// ```rust - /// # let opt = Some(1); - /// # let res: Result = Ok(1); - /// if let Some(x) = opt { - /// // .. - /// } - /// - /// if let Ok(x) = res { - /// // .. - /// } - /// ``` - #[clippy::version = "1.45.0"] - pub FOR_LOOPS_OVER_FALLIBLES, - suspicious, - "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" -} - declare_clippy_lint! { /// ### What it does /// Detects `loop + match` combinations that are easier @@ -648,7 +604,6 @@ declare_lint_pass!(Loops => [ EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, - FOR_LOOPS_OVER_FALLIBLES, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -739,30 +694,22 @@ fn check_for_loop<'tcx>( manual_find::check(cx, pat, arg, body, span, expr); } -fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { - let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used - +fn check_for_loop_arg(cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) { if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind { let method_name = method.ident.as_str(); // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x match method_name { "iter" | "iter_mut" => { explicit_iter_loop::check(cx, self_arg, arg, method_name); - for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); }, "into_iter" => { explicit_iter_loop::check(cx, self_arg, arg, method_name); explicit_into_iter_loop::check(cx, self_arg, arg); - for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); }, "next" => { - next_loop_linted = iter_next_loop::check(cx, arg); + iter_next_loop::check(cx, arg); }, _ => {}, } } - - if !next_loop_linted { - for_loops_over_fallibles::check(cx, pat, arg, None); - } } diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index d320eea1c377..76d6ad0b23e6 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -11,8 +11,8 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::disallowed_method", "clippy::disallowed_methods"), ("clippy::disallowed_type", "clippy::disallowed_types"), ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), - ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), @@ -31,6 +31,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::to_string_in_display", "clippy::recursive_format_impl"), ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::drop_bounds", "drop_bounds"), + ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), diff --git a/src/docs.rs b/src/docs.rs index 3bf488ab4779..bd27bc7938f8 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -170,7 +170,6 @@ docs! { "fn_to_numeric_cast_any", "fn_to_numeric_cast_with_truncation", "for_kv_map", - "for_loops_over_fallibles", "forget_copy", "forget_non_drop", "forget_ref", diff --git a/src/docs/for_loops_over_fallibles.txt b/src/docs/for_loops_over_fallibles.txt deleted file mode 100644 index c5a7508e45d4..000000000000 --- a/src/docs/for_loops_over_fallibles.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for `for` loops over `Option` or `Result` values. - -### Why is this bad? -Readability. This is more clearly expressed as an `if -let`. - -### Example -``` -for x in opt { - // .. -} - -for x in &res { - // .. -} - -for x in res.iter() { - // .. -} -``` - -Use instead: -``` -if let Some(x) = opt { - // .. -} - -if let Ok(x) = res { - // .. -} -``` \ No newline at end of file diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs deleted file mode 100644 index 75cdcc02353f..000000000000 --- a/tests/ui/for_loops_over_fallibles.rs +++ /dev/null @@ -1,74 +0,0 @@ -#![warn(clippy::for_loops_over_fallibles)] -#![allow(clippy::uninlined_format_args)] -#![allow(for_loops_over_fallibles)] - -fn for_loops_over_fallibles() { - let option = Some(1); - let mut result = option.ok_or("x not found"); - let v = vec![0, 1, 2]; - - // check over an `Option` - for x in option { - println!("{}", x); - } - - // check over an `Option` - for x in option.iter() { - println!("{}", x); - } - - // check over a `Result` - for x in result { - println!("{}", x); - } - - // check over a `Result` - for x in result.iter_mut() { - println!("{}", x); - } - - // check over a `Result` - for x in result.into_iter() { - println!("{}", x); - } - - for x in option.ok_or("x not found") { - println!("{}", x); - } - - // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call - // in the chain - for x in v.iter().next() { - println!("{}", x); - } - - // make sure we lint when next() is not the last call in the chain - for x in v.iter().next().and(Some(0)) { - println!("{}", x); - } - - for x in v.iter().next().ok_or("x not found") { - println!("{}", x); - } - - // check for false positives - - // for loop false positive - for x in v { - println!("{}", x); - } - - // while let false positive for Option - while let Some(x) = option { - println!("{}", x); - break; - } - - // while let false positive for Result - while let Ok(x) = result { - println!("{}", x); - break; - } -} - -fn main() {} diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr deleted file mode 100644 index f09adccabd1a..000000000000 --- a/tests/ui/for_loops_over_fallibles.stderr +++ /dev/null @@ -1,95 +0,0 @@ -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:10:14 - | -LL | for x in option { - | ^^^^^^ - | - = help: consider replacing `for x in option` with `if let Some(x) = option` - = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` - -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:15:14 - | -LL | for x in option.iter() { - | ^^^^^^ - | - = help: consider replacing `for x in option.iter()` with `if let Some(x) = option` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:20:14 - | -LL | for x in result { - | ^^^^^^ - | - = help: consider replacing `for x in result` with `if let Ok(x) = result` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:25:14 - | -LL | for x in result.iter_mut() { - | ^^^^^^ - | - = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:30:14 - | -LL | for x in result.into_iter() { - | ^^^^^^ - | - = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` - -error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:34:14 - | -LL | for x in option.ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` - -error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loops_over_fallibles.rs:40:14 - | -LL | for x in v.iter().next() { - | ^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::iter_next_loop)]` on by default - -error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:45:14 - | -LL | for x in v.iter().next().and(Some(0)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` - -error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:49:14 - | -LL | for x in v.iter().next().ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` - -error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:61:5 - | -LL | / while let Some(x) = option { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - | - = note: `#[deny(clippy::never_loop)]` on by default - -error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:67:5 - | -LL | / while let Ok(x) = result { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - -error: aborting due to 11 previous errors - diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index a59da4ae10bc..e12ea7ec1450 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -7,7 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, - clippy::for_loops_over_fallibles, + for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 0bdbefa51e8b..325a6db06c4e 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -7,7 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, - clippy::for_loops_over_fallibles, + for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index a6e7bdba77c6..8beae8dee085 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -12,7 +12,7 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::for_loops_over_fallibles)] +#![allow(for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] #![allow(clippy::overly_complex_bool_expr)] @@ -45,8 +45,8 @@ #![warn(clippy::disallowed_methods)] #![warn(clippy::disallowed_types)] #![warn(clippy::mixed_read_write_in_expression)] -#![warn(clippy::for_loops_over_fallibles)] -#![warn(clippy::for_loops_over_fallibles)] +#![warn(for_loops_over_fallibles)] +#![warn(for_loops_over_fallibles)] #![warn(clippy::useless_conversion)] #![warn(clippy::match_result_ok)] #![warn(clippy::overly_complex_bool_expr)] @@ -65,6 +65,7 @@ #![warn(clippy::recursive_format_impl)] #![warn(clippy::invisible_characters)] #![warn(drop_bounds)] +#![warn(for_loops_over_fallibles)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index e8f57597d02b..9e665047baae 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -12,7 +12,7 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::for_loops_over_fallibles)] +#![allow(for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] #![allow(clippy::overly_complex_bool_expr)] @@ -65,6 +65,7 @@ #![warn(clippy::to_string_in_display)] #![warn(clippy::zero_width_space)] #![warn(clippy::drop_bounds)] +#![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] #![warn(clippy::invalid_ref)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 31865a7f66d6..63eb565185f0 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -54,17 +54,17 @@ error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_r LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` -error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` -error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` --> $DIR/rename.rs:50:9 @@ -174,59 +174,65 @@ error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` +error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:68:9 | +LL | #![warn(clippy::for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` + +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` + --> $DIR/rename.rs:69:9 + | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 38 previous errors +error: aborting due to 39 previous errors From 7a42219e3741cf260e7e0169126869a5f3868c96 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 10 Oct 2022 02:05:24 +0000 Subject: [PATCH 017/524] Rename AssocItemKind::TyAlias to AssocItemKind::Type --- clippy_utils/src/ast_utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 493991f30e87..0133997560ea 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -438,14 +438,14 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r)) }, ( - TyAlias(box ast::TyAlias { + Type(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt, .. }), - TyAlias(box ast::TyAlias { + Type(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, From c3f077c7ad58e3dae45fc09e9645c235337c3419 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Oct 2022 20:45:04 +0200 Subject: [PATCH 018/524] Fix unclosed HTML tag in clippy doc --- clippy_utils/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 42374fdd7baf..e7e3625c078a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2064,9 +2064,9 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } } -/// Returns Option where String is a textual representation of the type encapsulated in the -/// slice iff the given expression is a slice of primitives (as defined in the -/// `is_recursively_primitive_type` function) and None otherwise. +/// Returns `Option` where String is a textual representation of the type encapsulated in +/// the slice iff the given expression is a slice of primitives (as defined in the +/// `is_recursively_primitive_type` function) and `None` otherwise. pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let expr_type = cx.typeck_results().expr_ty_adjusted(expr); let expr_kind = expr_type.kind(); From bd61fdbd5f4f436c3bf5886f757ce6a2d45b40df Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Mon, 10 Oct 2022 13:05:07 +0200 Subject: [PATCH 019/524] fix `box-default` ignoring trait objects' types --- clippy_lints/src/box_default.rs | 2 +- tests/ui/box_default.fixed | 16 +++++++++++++++- tests/ui/box_default.rs | 16 +++++++++++++++- tests/ui/box_default.stderr | 8 +++++++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index f35a79dcc739..4c0ccd08ef25 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -88,7 +88,7 @@ struct InferVisitor(bool); impl<'tcx> Visitor<'tcx> for InferVisitor { fn visit_ty(&mut self, t: &rustc_hir::Ty<'_>) { - self.0 |= matches!(t.kind, TyKind::Infer); + self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..)); if !self.0 { walk_ty(self, t); } diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed index 7fbb272ce5a3..911fa856aa0a 100644 --- a/tests/ui/box_default.fixed +++ b/tests/ui/box_default.fixed @@ -40,4 +40,18 @@ fn ret_ty_fn() -> Box { } #[allow(clippy::boxed_local)] -fn call_ty_fn(_b: Box) {} +fn call_ty_fn(_b: Box) { + issue_9621_dyn_trait(); +} + +use std::io::{Read, Result}; + +impl Read for ImplementsDefault { + fn read(&mut self, _: &mut [u8]) -> Result { + Ok(0) + } +} + +fn issue_9621_dyn_trait() { + let _: Box = Box::::default(); +} diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index 64c4f3887af7..20019c2ee5a0 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -40,4 +40,18 @@ fn ret_ty_fn() -> Box { } #[allow(clippy::boxed_local)] -fn call_ty_fn(_b: Box) {} +fn call_ty_fn(_b: Box) { + issue_9621_dyn_trait(); +} + +use std::io::{Read, Result}; + +impl Read for ImplementsDefault { + fn read(&mut self, _: &mut [u8]) -> Result { + Ok(0) + } +} + +fn issue_9621_dyn_trait() { + let _: Box = Box::new(ImplementsDefault::default()); +} diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index 313255fc950e..5ea410331afb 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -78,5 +78,11 @@ error: `Box::new(_)` of default value LL | Box::new(bool::default()) | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` -error: aborting due to 13 previous errors +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:56:28 + | +LL | let _: Box = Box::new(ImplementsDefault::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: aborting due to 14 previous errors From 9ebd6916125e9181d1ce340a159e9403afc99188 Mon Sep 17 00:00:00 2001 From: Cody Date: Mon, 10 Oct 2022 23:37:09 -0500 Subject: [PATCH 020/524] Fix allow_attributes_without_reason applying to external crate macros Previously the `clippy::allow_attributes_without_reason` lint would apply to external crate macros. Many macros in the Rust ecosystem include these `allow` attributes without adding a reason, making this lint pretty much unusable in any sizable Rust project. This commit fixes that by adding a check to the lint if the attribute is from an external crate macro and returning early. --- clippy_lints/src/attrs.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0bd1f8b784e8..bed9cf565f30 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -464,6 +464,11 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem return; } + // Check if the attribute is in an external macro and therefore out of the developer's control + if in_external_macro(cx.sess(), attr.span) { + return; + } + span_lint_and_help( cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, From 524ec2e12577d652875c68c3f205e8530451ba48 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 11 Oct 2022 19:41:44 -0400 Subject: [PATCH 021/524] Fix bug in `referent_used_exactly_once` --- clippy_lints/src/dereference.rs | 8 ++++---- clippy_utils/src/mir/mod.rs | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 45ee2fce4e47..82fc41d3a82f 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1194,16 +1194,16 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { }) } -fn referent_used_exactly_once<'a, 'tcx>( - cx: &'a LateContext<'tcx>, +fn referent_used_exactly_once<'tcx>( + cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, reference: &Expr<'tcx>, ) -> bool { let mir = enclosing_mir(cx.tcx, reference.hir_id); if let Some(local) = expr_local(cx.tcx, reference) && let [location] = *local_assignments(mir, local).as_slice() - && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = - mir.basic_blocks[location.block].statements[location.statement_index].kind + && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind && !place.has_deref() { let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index c8aa6f3e595d..818e603f665e 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -121,8 +121,7 @@ pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { }) } -/// Returns a vector of `mir::Location` where `local` is assigned. Each statement referred to has -/// kind `StatementKind::Assign`. +/// Returns a vector of `mir::Location` where `local` is assigned. pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec { let mut locations = Vec::new(); for (block, data) in mir.basic_blocks.iter_enumerated() { From e51e9308d58be45bffaf05c66af71681aaafd431 Mon Sep 17 00:00:00 2001 From: kraktus Date: Wed, 12 Oct 2022 12:04:41 +0200 Subject: [PATCH 022/524] `default_numeric_fallback` do not lint on constants --- clippy_lints/src/default_numeric_fallback.rs | 27 +++++++++------- tests/ui/default_numeric_fallback_f64.fixed | 9 ++++++ tests/ui/default_numeric_fallback_f64.rs | 9 ++++++ tests/ui/default_numeric_fallback_f64.stderr | 34 ++++++++++++-------- tests/ui/default_numeric_fallback_i32.fixed | 10 ++++++ tests/ui/default_numeric_fallback_i32.rs | 10 ++++++ tests/ui/default_numeric_fallback_i32.stderr | 34 ++++++++++++-------- 7 files changed, 93 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 3ed9cd36a229..8b5c9d25dc85 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; +use clippy_utils::{get_parent_node, numeric_literal}; use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, walk_stmt, Visitor}, - Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, + Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{ @@ -55,7 +55,12 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { - let mut visitor = NumericFallbackVisitor::new(cx); + let is_parent_const = if let Some(Node::Item(item)) = get_parent_node(cx.tcx, body.id().hir_id) { + matches!(item.kind, ItemKind::Const(..)) + } else { + false + }; + let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const); visitor.visit_body(body); } } @@ -68,9 +73,13 @@ struct NumericFallbackVisitor<'a, 'tcx> { } impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>, is_parent_const: bool) -> Self { Self { - ty_bounds: vec![TyBound::Nothing], + ty_bounds: vec![if is_parent_const { + TyBound::Any + } else { + TyBound::Nothing + }], cx, } } @@ -192,13 +201,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(local) => { - if local.ty.is_some() { - self.ty_bounds.push(TyBound::Any); - } else { - self.ty_bounds.push(TyBound::Nothing); - } - }, + StmtKind::Local(local) if local.ty.is_some() => self.ty_bounds.push(TyBound::Any), _ => self.ty_bounds.push(TyBound::Nothing), } diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index a28bff76755b..a370ccc76962 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -33,6 +33,7 @@ mod basic_expr { let x: [f64; 3] = [1., 2., 3.]; let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) }; let x: _ = 1.; + const X: f32 = 1.; } } @@ -59,6 +60,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2. }; + + const X: f32 = { + // Should lint this because this literal is not bound to any types. + let y = 1.0_f64; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1. + }; } } diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index b48435cc7b28..2476fe95141d 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -33,6 +33,7 @@ mod basic_expr { let x: [f64; 3] = [1., 2., 3.]; let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) }; let x: _ = 1.; + const X: f32 = 1.; } } @@ -59,6 +60,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2. }; + + const X: f32 = { + // Should lint this because this literal is not bound to any types. + let y = 1.; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1. + }; } } diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index f8b6c7746edb..5df2f642388d 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -61,79 +61,85 @@ LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:43:21 + --> $DIR/default_numeric_fallback_f64.rs:44:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:51:21 + --> $DIR/default_numeric_fallback_f64.rs:52:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:57:21 + --> $DIR/default_numeric_fallback_f64.rs:58:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:69:9 + --> $DIR/default_numeric_fallback_f64.rs:66:21 + | +LL | let y = 1.; + | ^^ help: consider adding suffix: `1.0_f64` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_f64.rs:78:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:75:27 + --> $DIR/default_numeric_fallback_f64.rs:84:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:79:29 + --> $DIR/default_numeric_fallback_f64.rs:88:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:93:21 + --> $DIR/default_numeric_fallback_f64.rs:102:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:96:32 + --> $DIR/default_numeric_fallback_f64.rs:105:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:114:28 + --> $DIR/default_numeric_fallback_f64.rs:123:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:117:36 + --> $DIR/default_numeric_fallback_f64.rs:126:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:135:24 + --> $DIR/default_numeric_fallback_f64.rs:144:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:155:23 + --> $DIR/default_numeric_fallback_f64.rs:164:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:162:21 + --> $DIR/default_numeric_fallback_f64.rs:171:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` @@ -143,5 +149,5 @@ LL | internal_macro!(); | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 23 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index 55451cf2f7d0..3f4994f0453b 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -33,6 +33,8 @@ mod basic_expr { let x: [i32; 3] = [1, 2, 3]; let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; let x: _ = 1; + let x: u64 = 1; + const CONST_X: i8 = 1; } } @@ -59,6 +61,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2 }; + + const CONST_X: i32 = { + // Should lint this because this literal is not bound to any types. + let y = 1_i32; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; } } diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 62d72f2febaa..2df0e09787f9 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -33,6 +33,8 @@ mod basic_expr { let x: [i32; 3] = [1, 2, 3]; let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; let x: _ = 1; + let x: u64 = 1; + const CONST_X: i8 = 1; } } @@ -59,6 +61,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2 }; + + const CONST_X: i32 = { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; } } diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index f7c5e724c403..6f219c3fc2b0 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -73,79 +73,85 @@ LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:43:21 + --> $DIR/default_numeric_fallback_i32.rs:45:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:51:21 + --> $DIR/default_numeric_fallback_i32.rs:53:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:57:21 + --> $DIR/default_numeric_fallback_i32.rs:59:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:69:9 + --> $DIR/default_numeric_fallback_i32.rs:67:21 + | +LL | let y = 1; + | ^ help: consider adding suffix: `1_i32` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_i32.rs:79:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:75:27 + --> $DIR/default_numeric_fallback_i32.rs:85:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:79:29 + --> $DIR/default_numeric_fallback_i32.rs:89:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:93:21 + --> $DIR/default_numeric_fallback_i32.rs:103:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:96:32 + --> $DIR/default_numeric_fallback_i32.rs:106:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:114:28 + --> $DIR/default_numeric_fallback_i32.rs:124:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:117:36 + --> $DIR/default_numeric_fallback_i32.rs:127:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:135:24 + --> $DIR/default_numeric_fallback_i32.rs:145:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:155:23 + --> $DIR/default_numeric_fallback_i32.rs:165:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:162:21 + --> $DIR/default_numeric_fallback_i32.rs:172:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -155,5 +161,5 @@ LL | internal_macro!(); | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 25 previous errors +error: aborting due to 26 previous errors From 36b2685977bdd731a0ef28e12bf8ef0334507d9e Mon Sep 17 00:00:00 2001 From: kraktus Date: Wed, 12 Oct 2022 23:00:52 +0200 Subject: [PATCH 023/524] refactor `default_numeric_fallback` We only need to store if the literal binding has an explicit type bound or not --- clippy_lints/src/default_numeric_fallback.rs | 45 ++++++++------------ 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 8b5c9d25dc85..199f8e10e549 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { struct NumericFallbackVisitor<'a, 'tcx> { /// Stack manages type bound of exprs. The top element holds current expr type. - ty_bounds: Vec>, + ty_bounds: Vec, cx: &'a LateContext<'tcx>, } @@ -76,9 +76,9 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>, is_parent_const: bool) -> Self { Self { ty_bounds: vec![if is_parent_const { - TyBound::Any + ExplicitTyBound(true) } else { - TyBound::Nothing + ExplicitTyBound(false) }], cx, } @@ -88,10 +88,10 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { if_chain! { if !in_external_macro(self.cx.sess(), lit.span); - if let Some(ty_bound) = self.ty_bounds.last(); + if let Some(explicit_ty_bounds) = self.ty_bounds.last(); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - if !ty_bound.is_numeric(); + if !explicit_ty_bounds.0; then { let (suffix, is_float) = match lit_ty.kind() { ty::Int(IntTy::I32) => ("i32", false), @@ -132,7 +132,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) { for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) { // Push found arg type, then visit arg. - self.ty_bounds.push(TyBound::Ty(*bound)); + self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); } @@ -144,7 +144,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) { let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder(); for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) { - self.ty_bounds.push(TyBound::Ty(*bound)); + self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); } @@ -178,7 +178,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { // Visit base with no bound. if let Some(base) = base { - self.ty_bounds.push(TyBound::Nothing); + self.ty_bounds.push(ExplicitTyBound(false)); self.visit_expr(base); self.ty_bounds.pop(); } @@ -201,9 +201,10 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(local) if local.ty.is_some() => self.ty_bounds.push(TyBound::Any), + // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` + StmtKind::Local(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())), - _ => self.ty_bounds.push(TyBound::Nothing), + _ => self.ty_bounds.push(ExplicitTyBound(false)), } walk_stmt(self, stmt); @@ -221,28 +222,18 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option { - Any, - Ty(Ty<'tcx>), - Nothing, -} +struct ExplicitTyBound(pub bool); -impl<'tcx> TyBound<'tcx> { - fn is_numeric(self) -> bool { - match self { - TyBound::Any => true, - TyBound::Ty(t) => t.is_numeric(), - TyBound::Nothing => false, - } +impl<'tcx> From> for ExplicitTyBound { + fn from(v: Ty<'tcx>) -> Self { + Self(v.is_numeric()) } } -impl<'tcx> From>> for TyBound<'tcx> { +impl<'tcx> From>> for ExplicitTyBound { fn from(v: Option>) -> Self { - match v { - Some(t) => TyBound::Ty(t), - None => TyBound::Nothing, - } + Self(v.map_or(false, Ty::is_numeric)) } } From 135a2730eb1750cff33e79a2e22e5730a2e0a60c Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Thu, 13 Oct 2022 23:48:05 -0500 Subject: [PATCH 024/524] Book: Small grammar + link a11y change --- book/src/development/basics.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/book/src/development/basics.md b/book/src/development/basics.md index 44ba6e32755e..6fb53236e6f1 100644 --- a/book/src/development/basics.md +++ b/book/src/development/basics.md @@ -69,7 +69,7 @@ the reference file with: cargo dev bless ``` -For example, this is necessary, if you fix a typo in an error message of a lint +For example, this is necessary if you fix a typo in an error message of a lint, or if you modify a test file to add a test case. > _Note:_ This command may update more files than you intended. In that case @@ -101,8 +101,9 @@ cargo dev setup intellij cargo dev dogfood ``` -More about intellij command usage and reasons -[here](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust) +More about [intellij] command usage and reasons. + +[intellij]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust ## lintcheck From 4f50e6f41ec8b55a6dc52066dc15fa5d17b8e39b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 14 Sep 2022 23:42:25 +0000 Subject: [PATCH 025/524] Remove CastCheckResult since it's unused --- clippy_lints/src/transmute/utils.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index b567d92230bb..102f7541c8ce 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,15 +1,16 @@ use rustc_hir::Expr; -use rustc_hir_analysis::check::{ - cast::{self, CastCheckResult}, - FnCtxt, Inherited, -}; +use rustc_hir_analysis::check::{cast, FnCtxt, Inherited}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment -pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { +pub(super) fn is_layout_incompatible<'tcx>( + cx: &LateContext<'tcx>, + from: Ty<'tcx>, + to: Ty<'tcx>, +) -> bool { if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from) && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to) && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from)) @@ -32,7 +33,9 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, ) -> bool { - use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; + use CastKind::{ + AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast, + }; matches!( check_cast(cx, e, from_ty, to_ty), Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) @@ -43,7 +46,12 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( /// the cast. In certain cases, including some invalid casts from array references /// to pointers, this may cause additional errors to be emitted and/or ICE error /// messages. This function will panic if that occurs. -fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option { +fn check_cast<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) -> Option { let hir_id = e.hir_id; let local_def_id = hir_id.owner.def_id; @@ -51,12 +59,9 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx> let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id); // If we already have errors, we can't be sure we can pointer cast. - assert!( - !fn_ctxt.errors_reported_since_creation(), - "Newly created FnCtxt contained errors" - ); + assert!(!fn_ctxt.errors_reported_since_creation(), "Newly created FnCtxt contained errors"); - if let CastCheckResult::Deferred(check) = cast::check_cast( + if let Ok(check) = cast::CastCheck::new( &fn_ctxt, e, from_ty, to_ty, // We won't show any error to the user, so we don't care what the span is here. DUMMY_SP, DUMMY_SP, From 950f5fa945beaa5ce08d1b7682c533b7fa57c417 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 14 Oct 2022 16:57:06 +0200 Subject: [PATCH 026/524] add missing comma --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c977b2cacab..85f94a74ad91 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,7 +29,7 @@ All contributors are expected to follow the [Rust Code of Conduct]. ## The Clippy book -If you're new to Clippy and don't know where to start the [Clippy book] includes +If you're new to Clippy and don't know where to start, the [Clippy book] includes a [developer guide] and is a good place to start your journey. [Clippy book]: https://doc.rust-lang.org/nightly/clippy/index.html From 344b7bca860ff9559b60d54350312e4ac1e88481 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 14 Oct 2022 13:21:59 -0400 Subject: [PATCH 027/524] Don't lint `ptr_arg` when used as an incompatible trait object --- clippy_lints/src/ptr.rs | 46 ++++++++++++++++++++++++++++++++++++++++- tests/ui/ptr_arg.rs | 30 ++++++++++++++++++++++++++- tests/ui/ptr_arg.stderr | 20 +++++++++++++++++- 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 2d80236327a1..b589ac4a1220 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -15,13 +15,17 @@ use rustc_hir::{ ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety, }; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Binder, ExistentialPredicate, List, PredicateKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::fmt; use std::iter; @@ -384,6 +388,17 @@ enum DerefTy<'tcx> { Slice(Option, Ty<'tcx>), } impl<'tcx> DerefTy<'tcx> { + fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> { + match *self { + Self::Str => cx.tcx.types.str_, + Self::Path => cx.tcx.mk_adt( + cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()), + List::empty(), + ), + Self::Slice(_, ty) => cx.tcx.mk_slice(ty), + } + } + fn argless_str(&self) -> &'static str { match *self { Self::Str => "str", @@ -581,6 +596,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0); if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| { match *ty.skip_binder().peel_refs().kind() { + ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds), ty::Param(_) => true, ty::Adt(def, _) => def.did() == args.ty_did, _ => false, @@ -614,6 +630,9 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: }; match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() { + ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => { + set_skip_flag(); + }, ty::Param(_) => { set_skip_flag(); }, @@ -665,6 +684,31 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: v.results } +fn matches_preds<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + preds: &'tcx [Binder<'tcx, ExistentialPredicate<'tcx>>], +) -> bool { + cx.tcx.infer_ctxt().enter(|infcx| { + preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) { + ExistentialPredicate::Trait(p) => infcx + .type_implements_trait(p.def_id, ty, p.substs, cx.param_env) + .must_apply_modulo_regions(), + ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new( + ObligationCause::dummy(), + cx.param_env, + cx.tcx.mk_predicate(Binder::bind_with_vars( + PredicateKind::Projection(p.with_self_ty(cx.tcx, ty)), + List::empty(), + )), + )), + ExistentialPredicate::AutoTrait(p) => infcx + .type_implements_trait(p, ty, List::empty(), cx.param_env) + .must_apply_modulo_regions(), + }) + }) +} + fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { if let TyKind::Rptr(lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index fd15001e540c..5f54101ca15a 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -3,7 +3,7 @@ #![warn(clippy::ptr_arg)] use std::borrow::Cow; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; fn do_vec(x: &Vec) { //Nothing here @@ -207,3 +207,31 @@ fn cow_conditional_to_mut(a: &mut Cow) { a.to_mut().push_str("foo"); } } + +// Issue #9542 +fn dyn_trait_ok(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + trait T {} + impl T for Vec {} + impl T for String {} + impl T for PathBuf {} + fn takes_dyn(_: &mut dyn T) {} + + takes_dyn(a); + takes_dyn(b); + takes_dyn(c); +} + +fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + trait T {} + impl T for Vec {} + impl T for [U] {} + impl T for String {} + impl T for str {} + impl T for PathBuf {} + impl T for Path {} + fn takes_dyn(_: &mut dyn T) {} + + takes_dyn(a); + takes_dyn(b); + takes_dyn(c); +} diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index d64b5f454a5a..6b4de98ce88c 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -162,5 +162,23 @@ error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a sl LL | fn mut_vec_slice_methods(v: &mut Vec) { | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` -error: aborting due to 17 previous errors +error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:17 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` + +error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:35 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^ help: change this to: `&mut str` + +error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:51 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^^ help: change this to: `&mut Path` + +error: aborting due to 20 previous errors From 4b8df8dc92c9e29ded57e1efa4d11d641e4b0ba7 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 14 Oct 2022 22:50:23 +0000 Subject: [PATCH 028/524] Add a suggestion and a note about orphan rules for `from_over_into` --- clippy_lints/src/from_over_into.rs | 176 ++++++++++++++++++++--- tests/ui/from_over_into.fixed | 62 ++++++++ tests/ui/from_over_into.rs | 41 ++++++ tests/ui/from_over_into.stderr | 57 +++++++- tests/ui/from_over_into_unfixable.rs | 35 +++++ tests/ui/from_over_into_unfixable.stderr | 29 ++++ 6 files changed, 375 insertions(+), 25 deletions(-) create mode 100644 tests/ui/from_over_into.fixed create mode 100644 tests/ui/from_over_into_unfixable.rs create mode 100644 tests/ui/from_over_into_unfixable.stderr diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 5d25c1d06341..95eda4ea8827 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,11 +1,19 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::span_is_local; +use clippy_utils::source::snippet_opt; +use clippy_utils::{meets_msrv, msrvs, path_def_id}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_path, Visitor}; +use rustc_hir::{ + GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty, + TyKind, +}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{kw, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -54,28 +62,152 @@ impl FromOverInto { impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { return; } - if_chain! { - if let hir::ItemKind::Impl{ .. } = &item.kind; - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.def_id); - if cx.tcx.is_diagnostic_item(sym::Into, impl_trait_ref.def_id); - - then { - span_lint_and_help( - cx, - FROM_OVER_INTO, - cx.tcx.sess.source_map().guess_head_span(item.span), - "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", - None, - &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()), - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(hir_trait_ref), + self_ty, + items: [impl_item_ref], + .. + }) = item.kind + && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() + // `impl Into for self_ty` + && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args + && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.def_id) + && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) + { + span_lint_and_then( + cx, + FROM_OVER_INTO, + cx.tcx.sess.source_map().guess_head_span(item.span), + "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", + |diag| { + // If the target type is likely foreign mention the orphan rules as it's a common source of confusion + if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) { + diag.help( + "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" + ); + } + + let message = format!("replace the `Into` implentation with `From<{}>`", middle_trait_ref.self_ty()); + if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) { + diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); + } else { + diag.help(message); + } + }, + ); } } extract_msrv_attr!(LateContext); } + +/// Finds the occurences of `Self` and `self` +struct SelfFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + /// Occurences of `Self` + upper: Vec, + /// Occurences of `self` + lower: Vec, + /// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding + /// already named `val` + invalid: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) { + for segment in path.segments { + match segment.ident.name { + kw::SelfLower => self.lower.push(segment.ident.span), + kw::SelfUpper => self.upper.push(segment.ident.span), + _ => continue, + } + } + + self.invalid |= path.span.from_expansion(); + if !self.invalid { + walk_path(self, path); + } + } + + fn visit_name(&mut self, name: Symbol) { + if name == sym::val { + self.invalid = true; + } + } +} + +fn convert_to_from( + cx: &LateContext<'_>, + into_trait_seg: &PathSegment<'_>, + target_ty: &Ty<'_>, + self_ty: &Ty<'_>, + impl_item_ref: &ImplItemRef, +) -> Option> { + let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id); + let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None }; + let body = cx.tcx.hir().body(body_id); + let [input] = body.params else { return None }; + let PatKind::Binding(.., self_ident, None) = input.pat.kind else { return None }; + + let from = snippet_opt(cx, self_ty.span)?; + let into = snippet_opt(cx, target_ty.span)?; + + let mut suggestions = vec![ + // impl Into for U -> impl From for U + // ~~~~ ~~~~ + (into_trait_seg.ident.span, String::from("From")), + // impl Into for U -> impl Into for U + // ~ ~ + (target_ty.span, from.clone()), + // impl Into for U -> impl Into for T + // ~ ~ + (self_ty.span, into), + // fn into(self) -> T -> fn from(self) -> T + // ~~~~ ~~~~ + (impl_item.ident.span, String::from("from")), + // fn into([mut] self) -> T -> fn into([mut] v: T) -> T + // ~~~~ ~~~~ + (self_ident.span, format!("val: {from}")), + // fn into(self) -> T -> fn into(self) -> Self + // ~ ~~~~ + (sig.decl.output.span(), String::from("Self")), + ]; + + let mut finder = SelfFinder { + cx, + upper: Vec::new(), + lower: Vec::new(), + invalid: false, + }; + finder.visit_expr(body.value); + + if finder.invalid { + return None; + } + + // don't try to replace e.g. `Self::default()` with `&[T]::default()` + if !finder.upper.is_empty() && !matches!(self_ty.kind, TyKind::Path(_)) { + return None; + } + + for span in finder.upper { + suggestions.push((span, from.clone())); + } + for span in finder.lower { + suggestions.push((span, String::from("val"))); + } + + Some(suggestions) +} diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed new file mode 100644 index 000000000000..e66dc43b0473 --- /dev/null +++ b/tests/ui/from_over_into.fixed @@ -0,0 +1,62 @@ +// run-rustfix + +#![warn(clippy::from_over_into)] +#![allow(unused)] + +// this should throw an error +struct StringWrapper(String); + +impl From for StringWrapper { + fn from(val: String) -> Self { + StringWrapper(val) + } +} + +struct SelfType(String); + +impl From for SelfType { + fn from(val: String) -> Self { + SelfType(String::new()) + } +} + +#[derive(Default)] +struct X; + +impl X { + const FOO: &'static str = "a"; +} + +struct SelfKeywords; + +impl From for SelfKeywords { + fn from(val: X) -> Self { + let _ = X::default(); + let _ = X::FOO; + let _: X = val; + + SelfKeywords + } +} + +struct ExplicitPaths(bool); + +impl core::convert::From for bool { + fn from(mut val: crate::ExplicitPaths) -> Self { + let in_closure = || val.0; + + val.0 = false; + val.0 + } +} + +// this is fine +struct A(String); + +impl From for A { + fn from(s: String) -> A { + A(s) + } +} + +fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 292d0924fb17..74c7be6af79e 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,4 +1,7 @@ +// run-rustfix + #![warn(clippy::from_over_into)] +#![allow(unused)] // this should throw an error struct StringWrapper(String); @@ -9,6 +12,44 @@ impl Into for String { } } +struct SelfType(String); + +impl Into for String { + fn into(self) -> SelfType { + SelfType(Self::new()) + } +} + +#[derive(Default)] +struct X; + +impl X { + const FOO: &'static str = "a"; +} + +struct SelfKeywords; + +impl Into for X { + fn into(self) -> SelfKeywords { + let _ = Self::default(); + let _ = Self::FOO; + let _: Self = self; + + SelfKeywords + } +} + +struct ExplicitPaths(bool); + +impl core::convert::Into for crate::ExplicitPaths { + fn into(mut self) -> bool { + let in_closure = || self.0; + + self.0 = false; + self.0 + } +} + // this is fine struct A(String); diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 469adadd2196..6cf83e258071 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,11 +1,62 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:6:1 + --> $DIR/from_over_into.rs:9:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider to implement `From` instead = note: `-D clippy::from-over-into` implied by `-D warnings` +help: replace the `Into` implentation with `From` + | +LL ~ impl From for StringWrapper { +LL ~ fn from(val: String) -> Self { +LL ~ StringWrapper(val) + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:17:1 + | +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From` + | +LL ~ impl From for SelfType { +LL ~ fn from(val: String) -> Self { +LL ~ SelfType(String::new()) + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:32:1 + | +LL | impl Into for X { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From` + | +LL ~ impl From for SelfKeywords { +LL ~ fn from(val: X) -> Self { +LL ~ let _ = X::default(); +LL ~ let _ = X::FOO; +LL ~ let _: X = val; + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:44:1 + | +LL | impl core::convert::Into for crate::ExplicitPaths { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence +help: replace the `Into` implentation with `From` + | +LL ~ impl core::convert::From for bool { +LL ~ fn from(mut val: crate::ExplicitPaths) -> Self { +LL ~ let in_closure = || val.0; +LL | +LL ~ val.0 = false; +LL ~ val.0 + | -error: aborting due to previous error +error: aborting due to 4 previous errors diff --git a/tests/ui/from_over_into_unfixable.rs b/tests/ui/from_over_into_unfixable.rs new file mode 100644 index 000000000000..3b280b7488ae --- /dev/null +++ b/tests/ui/from_over_into_unfixable.rs @@ -0,0 +1,35 @@ +#![warn(clippy::from_over_into)] + +struct InMacro(String); + +macro_rules! in_macro { + ($e:ident) => { + $e + }; +} + +impl Into for String { + fn into(self) -> InMacro { + InMacro(in_macro!(self)) + } +} + +struct WeirdUpperSelf; + +impl Into for &'static [u8] { + fn into(self) -> WeirdUpperSelf { + let _ = Self::default(); + WeirdUpperSelf + } +} + +struct ContainsVal; + +impl Into for ContainsVal { + fn into(self) -> u8 { + let val = 1; + val + 1 + } +} + +fn main() {} diff --git a/tests/ui/from_over_into_unfixable.stderr b/tests/ui/from_over_into_unfixable.stderr new file mode 100644 index 000000000000..6f6ce351921b --- /dev/null +++ b/tests/ui/from_over_into_unfixable.stderr @@ -0,0 +1,29 @@ +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:11:1 + | +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace the `Into` implentation with `From` + = note: `-D clippy::from-over-into` implied by `-D warnings` + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:19:1 + | +LL | impl Into for &'static [u8] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace the `Into` implentation with `From<&'static [u8]>` + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:28:1 + | +LL | impl Into for ContainsVal { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence + = help: replace the `Into` implentation with `From` + +error: aborting due to 3 previous errors + From dfd3525cff90fe2363494559499276ca07d2aef7 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 30 Sep 2022 21:10:10 -0400 Subject: [PATCH 029/524] Separate internal lints by pass --- clippy_lints/src/lib.register_internal.rs | 32 +- clippy_lints/src/lib.register_lints.rs | 32 +- clippy_lints/src/lib.rs | 28 +- clippy_lints/src/utils/internal_lints.rs | 1570 +---------------- .../internal_lints/clippy_lints_internal.rs | 49 + .../utils/internal_lints/collapsible_calls.rs | 240 +++ .../internal_lints/compiler_lint_functions.rs | 78 + .../utils/internal_lints/if_chain_style.rs | 161 ++ .../interning_defined_symbol.rs | 239 +++ .../src/utils/internal_lints/invalid_paths.rs | 105 ++ .../internal_lints/lint_without_lint_pass.rs | 346 ++++ .../internal_lints/metadata_collector.rs | 2 +- .../utils/internal_lints/msrv_attr_impl.rs | 63 + .../internal_lints/outer_expn_data_pass.rs | 62 + .../src/utils/internal_lints/produce_ice.rs | 37 + .../internal_lints/unnecessary_def_path.rs | 260 +++ tests/ui-internal/custom_ice_message.stderr | 2 +- 17 files changed, 1702 insertions(+), 1604 deletions(-) create mode 100644 clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs create mode 100644 clippy_lints/src/utils/internal_lints/collapsible_calls.rs create mode 100644 clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs create mode 100644 clippy_lints/src/utils/internal_lints/if_chain_style.rs create mode 100644 clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs create mode 100644 clippy_lints/src/utils/internal_lints/invalid_paths.rs create mode 100644 clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs create mode 100644 clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs create mode 100644 clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs create mode 100644 clippy_lints/src/utils/internal_lints/produce_ice.rs create mode 100644 clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs index 71dfdab369b9..40c94c6e8d33 100644 --- a/clippy_lints/src/lib.register_internal.rs +++ b/clippy_lints/src/lib.register_internal.rs @@ -3,20 +3,20 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), - LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS), - LintId::of(utils::internal_lints::DEFAULT_DEPRECATION_REASON), - LintId::of(utils::internal_lints::DEFAULT_LINT), - LintId::of(utils::internal_lints::IF_CHAIN_STYLE), - LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL), - LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::INVALID_PATHS), - LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL), - LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), - LintId::of(utils::internal_lints::PRODUCE_ICE), - LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH), - LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), + LintId::of(utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL), + LintId::of(utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS), + LintId::of(utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS), + LintId::of(utils::internal_lints::if_chain_style::IF_CHAIN_STYLE), + LintId::of(utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL), + LintId::of(utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR), + LintId::of(utils::internal_lints::invalid_paths::INVALID_PATHS), + LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON), + LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT), + LintId::of(utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE), + LintId::of(utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS), + LintId::of(utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE), + LintId::of(utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL), + LintId::of(utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA), + LintId::of(utils::internal_lints::produce_ice::PRODUCE_ICE), + LintId::of(utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH), ]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index de1253c8510a..5609a4dc9ea0 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -4,37 +4,37 @@ store.register_lints(&[ #[cfg(feature = "internal")] - utils::internal_lints::CLIPPY_LINTS_INTERNAL, + utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL, #[cfg(feature = "internal")] - utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, #[cfg(feature = "internal")] - utils::internal_lints::COMPILER_LINT_FUNCTIONS, + utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS, #[cfg(feature = "internal")] - utils::internal_lints::DEFAULT_DEPRECATION_REASON, + utils::internal_lints::if_chain_style::IF_CHAIN_STYLE, #[cfg(feature = "internal")] - utils::internal_lints::DEFAULT_LINT, + utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL, #[cfg(feature = "internal")] - utils::internal_lints::IF_CHAIN_STYLE, + utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR, #[cfg(feature = "internal")] - utils::internal_lints::INTERNING_DEFINED_SYMBOL, + utils::internal_lints::invalid_paths::INVALID_PATHS, #[cfg(feature = "internal")] - utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE, + utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON, #[cfg(feature = "internal")] - utils::internal_lints::INVALID_PATHS, + utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT, #[cfg(feature = "internal")] - utils::internal_lints::LINT_WITHOUT_LINT_PASS, + utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] - utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, + utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal")] - utils::internal_lints::MISSING_MSRV_ATTR_IMPL, + utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] - utils::internal_lints::OUTER_EXPN_EXPN_DATA, + utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, #[cfg(feature = "internal")] - utils::internal_lints::PRODUCE_ICE, + utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, #[cfg(feature = "internal")] - utils::internal_lints::UNNECESSARY_DEF_PATH, + utils::internal_lints::produce_ice::PRODUCE_ICE, #[cfg(feature = "internal")] - utils::internal_lints::UNNECESSARY_SYMBOL_STR, + utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH, almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, as_conversions::AS_CONVERSIONS, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ebb0f14fef52..b2ee58ec7ff7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -528,17 +528,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // all the internal lints #[cfg(feature = "internal")] { - store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); - store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); - store.register_late_pass(|_| Box::new(utils::internal_lints::CollapsibleCalls)); - store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new())); - store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle)); - store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath)); - store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl)); + store.register_early_pass(|| Box::new(utils::internal_lints::clippy_lints_internal::ClippyLintsInternal)); + store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce)); + store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls)); + store.register_late_pass(|_| { + Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new()) + }); + store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle)); + store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); + store.register_late_pass(|_| { + Box::::default() + }); + store.register_late_pass(|_| { + Box::::default() + }); + store.register_late_pass(|_| Box::new(utils::internal_lints::unnecessary_def_path::UnnecessaryDefPath)); + store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass)); + store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl)); } let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone(); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 0d908bf2a83e..71f6c9909ddd 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,1560 +1,12 @@ -use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; -use clippy_utils::consts::{constant_simple, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::match_type; -use clippy_utils::{ - def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_any_def_paths, - match_def_path, method_calls, paths, peel_blocks_with_stmt, peel_hir_expr_refs, SpanlessEq, -}; -use if_chain::if_chain; -use rustc_ast as ast; -use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId}; -use rustc_ast::visit::FnKind; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::hir_id::CRATE_HIR_ID; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{ - BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, - TyKind, UnOp, -}; -use rustc_hir_analysis::hir_ty_to_ty; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::nested_filter; -use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; -use rustc_middle::ty::{ - self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty, -}; -use rustc_semver::RustcVersion; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{sym, BytePos, Span}; - -use std::borrow::{Borrow, Cow}; -use std::str; - -#[cfg(feature = "internal")] +pub mod clippy_lints_internal; +pub mod collapsible_calls; +pub mod compiler_lint_functions; +pub mod if_chain_style; +pub mod interning_defined_symbol; +pub mod invalid_paths; +pub mod lint_without_lint_pass; pub mod metadata_collector; - -declare_clippy_lint! { - /// ### What it does - /// Checks for various things we like to keep tidy in clippy. - /// - /// ### Why is this bad? - /// We like to pretend we're an example of tidy code. - /// - /// ### Example - /// Wrong ordering of the util::paths constants. - pub CLIPPY_LINTS_INTERNAL, - internal, - "various things that will negatively affect your clippy experience" -} - -declare_clippy_lint! { - /// ### What it does - /// Ensures every lint is associated to a `LintPass`. - /// - /// ### Why is this bad? - /// The compiler only knows lints via a `LintPass`. Without - /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not - /// know the name of the lint. - /// - /// ### Known problems - /// Only checks for lints associated using the - /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. - /// - /// ### Example - /// ```rust,ignore - /// declare_lint! { pub LINT_1, ... } - /// declare_lint! { pub LINT_2, ... } - /// declare_lint! { pub FORGOTTEN_LINT, ... } - /// // ... - /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); - /// // missing FORGOTTEN_LINT - /// ``` - pub LINT_WITHOUT_LINT_PASS, - internal, - "declaring a lint without associating it in a LintPass" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` - /// variant of the function. - /// - /// ### Why is this bad? - /// The `utils::*` variants also add a link to the Clippy documentation to the - /// warning/error messages. - /// - /// ### Example - /// ```rust,ignore - /// cx.span_lint(LINT_NAME, "message"); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::span_lint(cx, LINT_NAME, "message"); - /// ``` - pub COMPILER_LINT_FUNCTIONS, - internal, - "usage of the lint functions of the compiler instead of the utils::* variant" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for calls to `cx.outer().expn_data()` and suggests to use - /// the `cx.outer_expn_data()` - /// - /// ### Why is this bad? - /// `cx.outer_expn_data()` is faster and more concise. - /// - /// ### Example - /// ```rust,ignore - /// expr.span.ctxt().outer().expn_data() - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// expr.span.ctxt().outer_expn_data() - /// ``` - pub OUTER_EXPN_EXPN_DATA, - internal, - "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" -} - -declare_clippy_lint! { - /// ### What it does - /// Not an actual lint. This lint is only meant for testing our customized internal compiler - /// error message by calling `panic`. - /// - /// ### Why is this bad? - /// ICE in large quantities can damage your teeth - /// - /// ### Example - /// ```rust,ignore - /// 🍦🍦🍦🍦🍦 - /// ``` - pub PRODUCE_ICE, - internal, - "this message should not appear anywhere as we ICE before and don't emit the lint" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for cases of an auto-generated lint without an updated description, - /// i.e. `default lint description`. - /// - /// ### Why is this bad? - /// Indicates that the lint is not finished. - /// - /// ### Example - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } - /// ``` - pub DEFAULT_LINT, - internal, - "found 'default lint description' in a lint declaration" -} - -declare_clippy_lint! { - /// ### What it does - /// Lints `span_lint_and_then` function calls, where the - /// closure argument has only one statement and that statement is a method - /// call to `span_suggestion`, `span_help`, `span_note` (using the same - /// span), `help` or `note`. - /// - /// These usages of `span_lint_and_then` should be replaced with one of the - /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or - /// `span_lint_and_note`. - /// - /// ### Why is this bad? - /// Using the wrapper `span_lint_and_*` functions, is more - /// convenient, readable and less error prone. - /// - /// ### Example - /// ```rust,ignore - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_suggestion( - /// expr.span, - /// help_msg, - /// sugg.to_string(), - /// Applicability::MachineApplicable, - /// ); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_help(expr.span, help_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.help(help_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_note(expr.span, note_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.note(note_msg); - /// }); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// span_lint_and_sugg( - /// cx, - /// TEST_LINT, - /// expr.span, - /// lint_msg, - /// help_msg, - /// sugg.to_string(), - /// Applicability::MachineApplicable, - /// ); - /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); - /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); - /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); - /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); - /// ``` - pub COLLAPSIBLE_SPAN_LINT_CALLS, - internal, - "found collapsible `span_lint_and_then` calls" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. - /// - /// ### Why is this bad? - /// The path for an item is subject to change and is less efficient to look up than a - /// diagnostic item or a `LangItem`. - /// - /// ### Example - /// ```rust,ignore - /// utils::match_type(cx, ty, &paths::VEC) - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) - /// ``` - pub UNNECESSARY_DEF_PATH, - internal, - "using a def path when a diagnostic item or a `LangItem` is available" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks the paths module for invalid paths. - /// - /// ### Why is this bad? - /// It indicates a bug in the code. - /// - /// ### Example - /// None. - pub INVALID_PATHS, - internal, - "invalid path" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for interning symbols that have already been pre-interned and defined as constants. - /// - /// ### Why is this bad? - /// It's faster and easier to use the symbol constant. - /// - /// ### Example - /// ```rust,ignore - /// let _ = sym!(f32); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// let _ = sym::f32; - /// ``` - pub INTERNING_DEFINED_SYMBOL, - internal, - "interning a symbol that is pre-interned and defined as a constant" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for unnecessary conversion from Symbol to a string. - /// - /// ### Why is this bad? - /// It's faster use symbols directly instead of strings. - /// - /// ### Example - /// ```rust,ignore - /// symbol.as_str() == "clippy"; - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// symbol == sym::clippy; - /// ``` - pub UNNECESSARY_SYMBOL_STR, - internal, - "unnecessary conversion between Symbol and string" -} - -declare_clippy_lint! { - /// Finds unidiomatic usage of `if_chain!` - pub IF_CHAIN_STYLE, - internal, - "non-idiomatic `if_chain!` usage" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for invalid `clippy::version` attributes. - /// - /// Valid values are: - /// * "pre 1.29.0" - /// * any valid semantic version - pub INVALID_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found an invalid `clippy::version` attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for declared clippy lints without the `clippy::version` attribute. - /// - pub MISSING_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found clippy lint without `clippy::version` attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. - /// - pub MISSING_MSRV_ATTR_IMPL, - internal, - "checking if all necessary steps were taken when adding a MSRV to a lint" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for cases of an auto-generated deprecated lint without an updated reason, - /// i.e. `"default deprecation note"`. - /// - /// ### Why is this bad? - /// Indicates that the documentation is incomplete. - /// - /// ### Example - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// TODO - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "default deprecation note" - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// This lint has been replaced by `cooler_lint` - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "this lint has been replaced by `cooler_lint`" - /// } - /// ``` - pub DEFAULT_DEPRECATION_REASON, - internal, - "found 'default deprecation note' in a deprecated lint declaration" -} - -declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); - -impl EarlyLintPass for ClippyLintsInternal { - fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { - if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { - if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { - let mut last_name: Option<&str> = None; - for item in items { - let name = item.ident.as_str(); - if let Some(last_name) = last_name { - if *last_name > *name { - span_lint( - cx, - CLIPPY_LINTS_INTERNAL, - item.span, - "this constant should be before the previous constant due to lexical \ - ordering", - ); - } - } - last_name = Some(name); - } - } - } - } - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct LintWithoutLintPass { - declared_lints: FxHashMap, - registered_lints: FxHashSet, -} - -impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); - -impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) - || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) - { - return; - } - - if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { - let is_lint_ref_ty = is_lint_ref_type(cx, ty); - if is_deprecated_lint(cx, ty) || is_lint_ref_ty { - check_invalid_clippy_version_attribute(cx, item); - - let expr = &cx.tcx.hir().body(body_id).value; - let fields; - if is_lint_ref_ty { - if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind - && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind { - fields = struct_fields; - } else { - return; - } - } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { - fields = struct_fields; - } else { - return; - } - - let field = fields - .iter() - .find(|f| f.ident.as_str() == "desc") - .expect("lints must have a description field"); - - if let ExprKind::Lit(Spanned { - node: LitKind::Str(ref sym, _), - .. - }) = field.expr.kind - { - let sym_str = sym.as_str(); - if is_lint_ref_ty { - if sym_str == "default lint description" { - span_lint( - cx, - DEFAULT_LINT, - item.span, - &format!("the lint `{}` has the default lint description", item.ident.name), - ); - } - - self.declared_lints.insert(item.ident.name, item.span); - } else if sym_str == "default deprecation note" { - span_lint( - cx, - DEFAULT_DEPRECATION_REASON, - item.span, - &format!("the lint `{}` has the default deprecation reason", item.ident.name), - ); - } - } - } - } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { - if !matches!( - cx.tcx.item_name(macro_call.def_id).as_str(), - "impl_lint_pass" | "declare_lint_pass" - ) { - return; - } - if let hir::ItemKind::Impl(hir::Impl { - of_trait: None, - items: impl_item_refs, - .. - }) = item.kind - { - let mut collector = LintCollector { - output: &mut self.registered_lints, - cx, - }; - let body_id = cx.tcx.hir().body_owned_by( - cx.tcx.hir().local_def_id( - impl_item_refs - .iter() - .find(|iiref| iiref.ident.as_str() == "get_lints") - .expect("LintPass needs to implement get_lints") - .id - .hir_id(), - ), - ); - collector.visit_expr(cx.tcx.hir().body(body_id).value); - } - } - } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) { - return; - } - - for (lint_name, &lint_span) in &self.declared_lints { - // When using the `declare_tool_lint!` macro, the original `lint_span`'s - // file points to "". - // `compiletest-rs` thinks that's an error in a different file and - // just ignores it. This causes the test in compile-fail/lint_pass - // not able to capture the error. - // Therefore, we need to climb the macro expansion tree and find the - // actual span that invoked `declare_tool_lint!`: - let lint_span = lint_span.ctxt().outer_expn_data().call_site; - - if !self.registered_lints.contains(lint_name) { - span_lint( - cx, - LINT_WITHOUT_LINT_PASS, - lint_span, - &format!("the lint `{lint_name}` is not added to any `LintPass`"), - ); - } - } - } -} - -fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Rptr( - _, - MutTy { - ty: inner, - mutbl: Mutability::Not, - }, - ) = ty.kind - { - if let TyKind::Path(ref path) = inner.kind { - if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); - } - } - } - - false -} - -fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { - if let Some(value) = extract_clippy_version_value(cx, item) { - // The `sym!` macro doesn't work as it only expects a single token. - // It's better to keep it this way and have a direct `Symbol::intern` call here. - if value == Symbol::intern("pre 1.29.0") { - return; - } - - if RustcVersion::parse(value.as_str()).is_err() { - span_lint_and_help( - cx, - INVALID_CLIPPY_VERSION_ATTRIBUTE, - item.span, - "this item has an invalid `clippy::version` attribute", - None, - "please use a valid semantic version, see `doc/adding_lints.md`", - ); - } - } else { - span_lint_and_help( - cx, - MISSING_CLIPPY_VERSION_ATTRIBUTE, - item.span, - "this lint is missing the `clippy::version` attribute or version value", - None, - "please use a `clippy::version` attribute, see `doc/adding_lints.md`", - ); - } -} - -/// This function extracts the version value of a `clippy::version` attribute if the given value has -/// one -fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - attrs.iter().find_map(|attr| { - if_chain! { - // Identify attribute - if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; - if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; - if tool_name.ident.name == sym::clippy; - if attr_name.ident.name == sym::version; - if let Some(version) = attr.value_str(); - then { - Some(version) - } else { - None - } - } - }) -} - -struct LintCollector<'a, 'tcx> { - output: &'a mut FxHashSet, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { - type NestedFilter = nested_filter::All; - - fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { - if path.segments.len() == 1 { - self.output.insert(path.segments[0].ident.name); - } - } - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } -} - -#[derive(Clone, Default)] -pub struct CompilerLintFunctions { - map: FxHashMap<&'static str, &'static str>, -} - -impl CompilerLintFunctions { - #[must_use] - pub fn new() -> Self { - let mut map = FxHashMap::default(); - map.insert("span_lint", "utils::span_lint"); - map.insert("struct_span_lint", "utils::span_lint"); - map.insert("lint", "utils::span_lint"); - map.insert("span_lint_note", "utils::span_lint_and_note"); - map.insert("span_lint_help", "utils::span_lint_and_help"); - Self { map } - } -} - -impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); - -impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind; - let fn_name = path.ident; - if let Some(sugg) = self.map.get(fn_name.as_str()); - let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, ty, &paths::EARLY_CONTEXT) - || match_type(cx, ty, &paths::LATE_CONTEXT); - then { - span_lint_and_help( - cx, - COMPILER_LINT_FUNCTIONS, - path.ident.span, - "usage of a compiler lint function", - None, - &format!("please use the Clippy variant of this function: `{sugg}`"), - ); - } - } - } -} - -declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); - -impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { - return; - } - - let (method_names, arg_lists, spans) = method_calls(expr, 2); - let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); - if_chain! { - if let ["expn_data", "outer_expn"] = method_names.as_slice(); - let (self_arg, args)= arg_lists[1]; - if args.is_empty(); - let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); - then { - span_lint_and_sugg( - cx, - OUTER_EXPN_EXPN_DATA, - spans[1].with_hi(expr.span.hi()), - "usage of `outer_expn().expn_data()`", - "try", - "outer_expn_data()".to_string(), - Applicability::MachineApplicable, - ); - } - } - } -} - -declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); - -impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { - assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); - } -} - -fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { - match fn_kind { - FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", - FnKind::Closure(..) => false, - } -} - -declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); - -impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(func, and_then_args) = expr.kind; - if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); - if and_then_args.len() == 5; - if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind; - let body = cx.tcx.hir().body(body); - let only_expr = peel_blocks_with_stmt(body.value); - if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; - if let ExprKind::Path(..) = recv.kind; - then { - let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).deny_side_effects(); - match ps.ident.as_str() { - "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); - }, - "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); - }, - "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); - }, - "help" => { - let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); - } - "note" => { - let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); - } - _ => (), - } - } - } - } -} - -struct AndThenSnippets<'a> { - cx: Cow<'a, str>, - lint: Cow<'a, str>, - span: Cow<'a, str>, - msg: Cow<'a, str>, -} - -fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { - let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); - let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); - let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); - let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); - - AndThenSnippets { - cx: cx_snippet, - lint: lint_snippet, - span: span_snippet, - msg: msg_snippet, - } -} - -struct SpanSuggestionSnippets<'a> { - help: Cow<'a, str>, - sugg: Cow<'a, str>, - applicability: Cow<'a, str>, -} - -fn span_suggestion_snippets<'a, 'hir>( - cx: &LateContext<'_>, - span_call_args: &'hir [Expr<'hir>], -) -> SpanSuggestionSnippets<'a> { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - let sugg_snippet = snippet(cx, span_call_args[2].span, ".."); - let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable"); - - SpanSuggestionSnippets { - help: help_snippet, - sugg: sugg_snippet, - applicability: applicability_snippet, - } -} - -fn suggest_suggestion( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - span_suggestion_snippets: &SpanSuggestionSnippets<'_>, -) { - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - span_suggestion_snippets.help, - span_suggestion_snippets.sugg, - span_suggestion_snippets.applicability - ), - Applicability::MachineApplicable, - ); -} - -fn suggest_help( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - help: &str, - with_span: bool, -) { - let option_span = if with_span { - format!("Some({})", and_then_snippets.span) - } else { - "None".to_string() - }; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_help({}, {}, {}, {}, {}, {help})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, - ), - Applicability::MachineApplicable, - ); -} - -fn suggest_note( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - note: &str, - with_span: bool, -) { - let note_span = if with_span { - format!("Some({})", and_then_snippets.span) - } else { - "None".to_string() - }; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, - ), - Applicability::MachineApplicable, - ); -} - -declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); - -#[allow(clippy::too_many_lines)] -impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - enum Item { - LangItem(Symbol), - DiagnosticItem(Symbol), - } - static PATHS: &[&[&str]] = &[ - &["clippy_utils", "match_def_path"], - &["clippy_utils", "match_trait_method"], - &["clippy_utils", "ty", "match_type"], - &["clippy_utils", "is_expr_path_def_path"], - ]; - - if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind; - if let ExprKind::Path(path) = &func.kind; - if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if let Some(which_path) = match_any_def_paths(cx, id, PATHS); - let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; - // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, item_arg); - let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id(); - then { - // def_path_res will match field names before anything else, but for this we want to match - // inherent functions first. - let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field { - let method_name = *segments.last().unwrap(); - cx.tcx.def_key(def_id).parent - .and_then(|parent_idx| - cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter() - .find_map(|impl_id| cx.tcx.associated_items(*impl_id) - .find_by_name_and_kind( - cx.tcx, - Ident::from_str(method_name), - AssocKind::Fn, - *impl_id, - ) - ) - ) - .map_or(def_id, |item| item.def_id) - } else { - def_id - }; - - // Check if the target item is a diagnostic item or LangItem. - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { - let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); - let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name; - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; - }; - - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } - _ => false, - }; - - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => - (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), - has_ctor - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false), - // match_type - (2, Item::DiagnosticItem(item)) => - (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (2, Item::LangItem(item)) => - (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!( - "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})", - ), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!( - "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})", - ), - false, - ), - (3, Item::DiagnosticItem(item)) => - (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", - ), - false, - ), - _ => return, - }; - - span_lint_and_then( - cx, - UNNECESSARY_DEF_PATH, - expr.span, - msg, - |diag| { - diag.span_suggestion(expr.span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead" - ); - } - }, - ); - } - } - } -} - -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { - match peel_hir_expr_refs(expr).0.kind { - ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => { - let parent_id = cx.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { - path_to_matched_type(cx, init) - } else { - None - } - }, - Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( - cx, - cx.tcx.eval_static_initializer(def_id).ok()?.inner(), - cx.tcx.type_of(def_id), - ), - Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { - read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) - }, - _ => None, - }, - _ => None, - }, - ExprKind::Array(exprs) => exprs - .iter() - .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some((*sym.as_str()).to_owned()); - } - } - - None - }) - .collect(), - _ => None, - } -} - -fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { - let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().values().next()?; - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { - (alloc.inner(), ty) - } else { - return None; - } - } else { - (alloc, ty) - }; - - if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() - && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() - && ty.is_str() - { - alloc - .provenance() - .values() - .map(|&alloc| { - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { - let alloc = alloc.inner(); - str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) - .ok().map(ToOwned::to_owned) - } else { - None - } - }) - .collect() - } else { - None - } -} - -// This is not a complete resolver for paths. It works on all the paths currently used in the paths -// module. That's all it does and all it needs to do. -pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if def_path_res(cx, path, None) != Res::Err { - return true; - } - - // Some implementations can't be found by `path_to_res`, particularly inherent - // implementations of native types. Check lang items. - let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); - let lang_items = cx.tcx.lang_items(); - // This list isn't complete, but good enough for our current list of paths. - let incoherent_impls = [ - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), - SimplifiedTypeGen::SliceSimplifiedType, - SimplifiedTypeGen::StrSimplifiedType, - ] - .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); - for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(*item_def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - if matches!( - cx.tcx.def_kind(*item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(*item_def_id) { - if child.ident.name == *item { - return true; - } - } - } else { - for child in cx.tcx.associated_item_def_ids(*item_def_id) { - if cx.tcx.item_name(*child) == *item { - return true; - } - } - } - } - } - } - - false -} - -declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidPaths { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let local_def_id = &cx.tcx.parent_module(item.hir_id()); - let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if_chain! { - if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(ty, body_id) = item.kind; - let ty = hir_ty_to_ty(cx.tcx, ty); - if let ty::Array(el_ty, _) = &ty.kind(); - if let ty::Ref(_, el_ty, _) = &el_ty.kind(); - if el_ty.is_str(); - let body = cx.tcx.hir().body(body_id); - let typeck_results = cx.tcx.typeck_body(body_id); - if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - let path: Vec<&str> = path.iter().map(|x| { - if let Constant::Str(s) = x { - s.as_str() - } else { - // We checked the type of the constant above - unreachable!() - } - }).collect(); - if !check_path(cx, &path[..]); - then { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } - } - } -} - -#[derive(Default)] -pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant DefId. - symbol_map: FxHashMap, -} - -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); - -impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { - fn check_crate(&mut self, cx: &LateContext<'_>) { - if !self.symbol_map.is_empty() { - return; - } - - for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { - for item in cx.tcx.module_children(def_id).iter() { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item_def_id); - } - } - } - } - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, [arg]) = &expr.kind; - if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); - if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); - if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); - let value = Symbol::intern(&arg).as_u32(); - if let Some(&def_id) = self.symbol_map.get(&value); - then { - span_lint_and_sugg( - cx, - INTERNING_DEFINED_SYMBOL, - is_expn_of(expr.span, "sym").unwrap_or(expr.span), - "interning a defined symbol", - "try", - cx.tcx.def_path_str(def_id), - Applicability::MachineApplicable, - ); - } - } - if let ExprKind::Binary(op, left, right) = expr.kind { - if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { - let data = [ - (left, self.symbol_str_expr(left, cx)), - (right, self.symbol_str_expr(right, cx)), - ]; - match data { - // both operands are a symbol string - [(_, Some(left)), (_, Some(right))] => { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary `Symbol` to string conversion", - "try", - format!( - "{} {} {}", - left.as_symbol_snippet(cx), - op.node.as_str(), - right.as_symbol_snippet(cx), - ), - Applicability::MachineApplicable, - ); - }, - // one of the operands is a symbol string - [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { - // creating an owned string for comparison - if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary string allocation", - "try", - format!("{}.as_str()", symbol.as_symbol_snippet(cx)), - Applicability::MachineApplicable, - ); - } - }, - // nothing found - [(_, None), (_, None)] => {}, - } - } - } - } -} - -impl InterningDefinedSymbol { - fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { - static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; - static SYMBOL_STR_PATHS: &[&[&str]] = &[ - &paths::SYMBOL_AS_STR, - &paths::SYMBOL_TO_IDENT_STRING, - &paths::TO_STRING_METHOD, - ]; - let call = if_chain! { - if let ExprKind::AddrOf(_, _, e) = expr.kind; - if let ExprKind::Unary(UnOp::Deref, e) = e.kind; - then { e } else { expr } - }; - if_chain! { - // is a method call - if let ExprKind::MethodCall(_, item, [], _) = call.kind; - if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); - let ty = cx.typeck_results().expr_ty(item); - // ...on either an Ident or a Symbol - if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { - Some(false) - } else if match_type(cx, ty, &paths::IDENT) { - Some(true) - } else { - None - }; - // ...which converts it to a string - let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; - if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); - then { - let is_to_owned = path.last().unwrap().ends_with("string"); - return Some(SymbolStrExpr::Expr { - item, - is_ident, - is_to_owned, - }); - } - } - // is a string constant - if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { - let value = Symbol::intern(&s).as_u32(); - // ...which matches a symbol constant - if let Some(&def_id) = self.symbol_map.get(&value) { - return Some(SymbolStrExpr::Const(def_id)); - } - } - None - } -} - -enum SymbolStrExpr<'tcx> { - /// a string constant with a corresponding symbol constant - Const(DefId), - /// a "symbol to string" expression like `symbol.as_str()` - Expr { - /// part that evaluates to `Symbol` or `Ident` - item: &'tcx Expr<'tcx>, - is_ident: bool, - /// whether an owned `String` is created like `to_ident_string()` - is_to_owned: bool, - }, -} - -impl<'tcx> SymbolStrExpr<'tcx> { - /// Returns a snippet that evaluates to a `Symbol` and is const if possible - fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { - match *self { - Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), - Self::Expr { item, is_ident, .. } => { - let mut snip = snippet(cx, item.span.source_callsite(), ".."); - if is_ident { - // get `Ident.name` - snip.to_mut().push_str(".name"); - } - snip - }, - } - } -} - -declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); - -impl<'tcx> LateLintPass<'tcx> for IfChainStyle { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - let (local, after, if_chain_span) = if_chain! { - if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; - if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); - then { (local, after, if_chain_span) } else { return } - }; - if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be above the `if_chain!`", - ); - } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be inside `then { .. }`", - ); - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { - (cond, then, r#else.is_some()) - } else { - return; - }; - let ExprKind::Block(then_block, _) = then.kind else { return }; - let if_chain_span = is_expn_of(expr.span, "if_chain"); - if !els { - check_nested_if_chains(cx, expr, then_block, if_chain_span); - } - let Some(if_chain_span) = if_chain_span else { return }; - // check for `if a && b;` - if_chain! { - if let ExprKind::Binary(op, _, _) = cond.kind; - if op.node == BinOpKind::And; - if cx.sess().source_map().is_multiline(cond.span); - then { - span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); - } - } - if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) - && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) - { - span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); - } - } -} - -fn check_nested_if_chains( - cx: &LateContext<'_>, - if_expr: &Expr<'_>, - then_block: &Block<'_>, - if_chain_span: Option, -) { - #[rustfmt::skip] - let (head, tail) = match *then_block { - Block { stmts, expr: Some(tail), .. } => (stmts, tail), - Block { - stmts: &[ - ref head @ .., - Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } - ], - .. - } => (head, tail), - _ => return, - }; - if_chain! { - if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); - let sm = cx.sess().source_map(); - if head - .iter() - .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); - if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); - then {} else { return } - } - let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { - (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), - (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), - (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), - _ => return, - }; - span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { - let (span, msg) = match head { - [] => return, - [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), - [a, .., b] => ( - a.span.to(b.span), - "these `let` statements can also be in the `if_chain!`", - ), - }; - diag.span_help(span, msg); - }); -} - -fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { - cx.tcx - .hir() - .parent_iter(hir_id) - .find(|(_, node)| { - #[rustfmt::skip] - !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) - }) - .map_or(false, |(id, _)| { - is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) - }) -} - -/// Checks a trailing slice of statements and expression of a `Block` to see if they are part -/// of the `then {..}` portion of an `if_chain!` -fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { - let span = if let [stmt, ..] = stmts { - stmt.span - } else if let Some(expr) = expr { - expr.span - } else { - // empty `then {}` - return true; - }; - is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) -} - -/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. -fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { - let mut span = local.pat.span; - if let Some(init) = local.init { - span = span.to(init.span); - } - span.adjust(if_chain_span.ctxt().outer_expn()); - let sm = cx.sess().source_map(); - let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span); - let span = sm.span_extend_to_next_char(span, ';', false); - Span::new( - span.lo() - BytePos(3), - span.hi() + BytePos(1), - span.ctxt(), - span.parent(), - ) -} - -declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); - -impl LateLintPass<'_> for MsrvAttrImpl { - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if_chain! { - if let hir::ItemKind::Impl(hir::Impl { - of_trait: Some(lint_pass_trait_ref), - self_ty, - items, - .. - }) = &item.kind; - if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); - let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); - if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); - let self_ty = hir_ty_to_ty(cx.tcx, self_ty); - if let ty::Adt(self_ty_def, _) = self_ty.kind(); - if self_ty_def.is_struct(); - if self_ty_def.all_fields().any(|f| { - cx.tcx - .type_of(f.did) - .walk() - .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) - }); - if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); - then { - let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; - let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; - let span = cx.sess().source_map().span_through_char(item.span, '{'); - span_lint_and_sugg( - cx, - MISSING_MSRV_ATTR_IMPL, - span, - &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), - &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), - format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), - Applicability::MachineApplicable, - ); - } - } - } -} +pub mod msrv_attr_impl; +pub mod outer_expn_data_pass; +pub mod produce_ice; +pub mod unnecessary_def_path; diff --git a/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs b/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs new file mode 100644 index 000000000000..da9514dd15ee --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs @@ -0,0 +1,49 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{Crate, ItemKind, ModKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for various things we like to keep tidy in clippy. + /// + /// ### Why is this bad? + /// We like to pretend we're an example of tidy code. + /// + /// ### Example + /// Wrong ordering of the util::paths constants. + pub CLIPPY_LINTS_INTERNAL, + internal, + "various things that will negatively affect your clippy experience" +} + +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); + +impl EarlyLintPass for ClippyLintsInternal { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { + if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { + if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { + if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { + let mut last_name: Option<&str> = None; + for item in items { + let name = item.ident.as_str(); + if let Some(last_name) = last_name { + if *last_name > *name { + span_lint( + cx, + CLIPPY_LINTS_INTERNAL, + item.span, + "this constant should be before the previous constant due to lexical \ + ordering", + ); + } + } + last_name = Some(name); + } + } + } + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs new file mode 100644 index 000000000000..c9089aecfa59 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -0,0 +1,240 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::{Closure, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use std::borrow::{Borrow, Cow}; + +declare_clippy_lint! { + /// ### What it does + /// Lints `span_lint_and_then` function calls, where the + /// closure argument has only one statement and that statement is a method + /// call to `span_suggestion`, `span_help`, `span_note` (using the same + /// span), `help` or `note`. + /// + /// These usages of `span_lint_and_then` should be replaced with one of the + /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or + /// `span_lint_and_note`. + /// + /// ### Why is this bad? + /// Using the wrapper `span_lint_and_*` functions, is more + /// convenient, readable and less error prone. + /// + /// ### Example + /// ```rust,ignore + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_suggestion( + /// expr.span, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_help(expr.span, help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.help(help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_note(expr.span, note_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.note(note_msg); + /// }); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// span_lint_and_sugg( + /// cx, + /// TEST_LINT, + /// expr.span, + /// lint_msg, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); + /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); + /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); + /// ``` + pub COLLAPSIBLE_SPAN_LINT_CALLS, + internal, + "found collapsible `span_lint_and_then` calls" +} + +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); + +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::Call(func, and_then_args) = expr.kind; + if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); + if and_then_args.len() == 5; + if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind; + let body = cx.tcx.hir().body(body); + let only_expr = peel_blocks_with_stmt(body.value); + if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; + if let ExprKind::Path(..) = recv.kind; + then { + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); + match ps.ident.as_str() { + "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); + }, + "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); + }, + "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); + }, + "help" => { + let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); + } + "note" => { + let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); + } + _ => (), + } + } + } + } +} + +struct AndThenSnippets<'a> { + cx: Cow<'a, str>, + lint: Cow<'a, str>, + span: Cow<'a, str>, + msg: Cow<'a, str>, +} + +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { + let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); + let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); + let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); + let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); + + AndThenSnippets { + cx: cx_snippet, + lint: lint_snippet, + span: span_snippet, + msg: msg_snippet, + } +} + +struct SpanSuggestionSnippets<'a> { + help: Cow<'a, str>, + sugg: Cow<'a, str>, + applicability: Cow<'a, str>, +} + +fn span_suggestion_snippets<'a, 'hir>( + cx: &LateContext<'_>, + span_call_args: &'hir [Expr<'hir>], +) -> SpanSuggestionSnippets<'a> { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + let sugg_snippet = snippet(cx, span_call_args[2].span, ".."); + let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable"); + + SpanSuggestionSnippets { + help: help_snippet, + sugg: sugg_snippet, + applicability: applicability_snippet, + } +} + +fn suggest_suggestion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + span_suggestion_snippets: &SpanSuggestionSnippets<'_>, +) { + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", + and_then_snippets.cx, + and_then_snippets.lint, + and_then_snippets.span, + and_then_snippets.msg, + span_suggestion_snippets.help, + span_suggestion_snippets.sugg, + span_suggestion_snippets.applicability + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_help( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + help: &str, + with_span: bool, +) { + let option_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_help({}, {}, {}, {}, {}, {help})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_note( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + note: &str, + with_span: bool, +) { + let note_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, + ), + Applicability::MachineApplicable, + ); +} diff --git a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs new file mode 100644 index 000000000000..67357a5cb6b0 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs @@ -0,0 +1,78 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::match_type; +use clippy_utils::{is_lint_allowed, paths}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` + /// variant of the function. + /// + /// ### Why is this bad? + /// The `utils::*` variants also add a link to the Clippy documentation to the + /// warning/error messages. + /// + /// ### Example + /// ```rust,ignore + /// cx.span_lint(LINT_NAME, "message"); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// utils::span_lint(cx, LINT_NAME, "message"); + /// ``` + pub COMPILER_LINT_FUNCTIONS, + internal, + "usage of the lint functions of the compiler instead of the utils::* variant" +} + +#[derive(Clone, Default)] +pub struct CompilerLintFunctions { + map: FxHashMap<&'static str, &'static str>, +} + +impl CompilerLintFunctions { + #[must_use] + pub fn new() -> Self { + let mut map = FxHashMap::default(); + map.insert("span_lint", "utils::span_lint"); + map.insert("struct_span_lint", "utils::span_lint"); + map.insert("lint", "utils::span_lint"); + map.insert("span_lint_note", "utils::span_lint_and_note"); + map.insert("span_lint_help", "utils::span_lint_and_help"); + Self { map } + } +} + +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); + +impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind; + let fn_name = path.ident; + if let Some(sugg) = self.map.get(fn_name.as_str()); + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + if match_type(cx, ty, &paths::EARLY_CONTEXT) + || match_type(cx, ty, &paths::LATE_CONTEXT); + then { + span_lint_and_help( + cx, + COMPILER_LINT_FUNCTIONS, + path.ident.span, + "usage of a compiler lint function", + None, + &format!("please use the Clippy variant of this function: `{sugg}`"), + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/clippy_lints/src/utils/internal_lints/if_chain_style.rs new file mode 100644 index 000000000000..a863fdc9d50c --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/if_chain_style.rs @@ -0,0 +1,161 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::{higher, is_else_clause, is_expn_of}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Span}; + +declare_clippy_lint! { + /// Finds unidiomatic usage of `if_chain!` + pub IF_CHAIN_STYLE, + internal, + "non-idiomatic `if_chain!` usage" +} + +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); + +impl<'tcx> LateLintPass<'tcx> for IfChainStyle { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + let (local, after, if_chain_span) = if_chain! { + if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; + if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); + then { (local, after, if_chain_span) } else { return } + }; + if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be above the `if_chain!`", + ); + } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be inside `then { .. }`", + ); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { + (cond, then, r#else.is_some()) + } else { + return; + }; + let ExprKind::Block(then_block, _) = then.kind else { return }; + let if_chain_span = is_expn_of(expr.span, "if_chain"); + if !els { + check_nested_if_chains(cx, expr, then_block, if_chain_span); + } + let Some(if_chain_span) = if_chain_span else { return }; + // check for `if a && b;` + if_chain! { + if let ExprKind::Binary(op, _, _) = cond.kind; + if op.node == BinOpKind::And; + if cx.sess().source_map().is_multiline(cond.span); + then { + span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); + } + } + if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) + && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) + { + span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); + } + } +} + +fn check_nested_if_chains( + cx: &LateContext<'_>, + if_expr: &Expr<'_>, + then_block: &Block<'_>, + if_chain_span: Option, +) { + #[rustfmt::skip] + let (head, tail) = match *then_block { + Block { stmts, expr: Some(tail), .. } => (stmts, tail), + Block { + stmts: &[ + ref head @ .., + Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } + ], + .. + } => (head, tail), + _ => return, + }; + if_chain! { + if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); + let sm = cx.sess().source_map(); + if head + .iter() + .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); + if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); + then {} else { return } + } + let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { + (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), + (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), + (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), + _ => return, + }; + span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { + let (span, msg) = match head { + [] => return, + [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), + [a, .., b] => ( + a.span.to(b.span), + "these `let` statements can also be in the `if_chain!`", + ), + }; + diag.span_help(span, msg); + }); +} + +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { + cx.tcx + .hir() + .parent_iter(hir_id) + .find(|(_, node)| { + #[rustfmt::skip] + !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) + }) + .map_or(false, |(id, _)| { + is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) + }) +} + +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part +/// of the `then {..}` portion of an `if_chain!` +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { + let span = if let [stmt, ..] = stmts { + stmt.span + } else if let Some(expr) = expr { + expr.span + } else { + // empty `then {}` + return true; + }; + is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) +} + +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { + let mut span = local.pat.span; + if let Some(init) = local.init { + span = span.to(init.span); + } + span.adjust(if_chain_span.ctxt().outer_expn()); + let sm = cx.sess().source_map(); + let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span); + let span = sm.span_extend_to_next_char(span, ';', false); + Span::new( + span.lo() - BytePos(3), + span.hi() + BytePos(1), + span.ctxt(), + span.parent(), + ) +} diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs new file mode 100644 index 000000000000..096b601572b4 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -0,0 +1,239 @@ +use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::match_type; +use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::{self}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::Symbol; + +use std::borrow::Cow; + +declare_clippy_lint! { + /// ### What it does + /// Checks for interning symbols that have already been pre-interned and defined as constants. + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. + /// + /// ### Example + /// ```rust,ignore + /// let _ = sym!(f32); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub INTERNING_DEFINED_SYMBOL, + internal, + "interning a symbol that is pre-interned and defined as a constant" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for unnecessary conversion from Symbol to a string. + /// + /// ### Why is this bad? + /// It's faster use symbols directly instead of strings. + /// + /// ### Example + /// ```rust,ignore + /// symbol.as_str() == "clippy"; + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// symbol == sym::clippy; + /// ``` + pub UNNECESSARY_SYMBOL_STR, + internal, + "unnecessary conversion between Symbol and string" +} + +#[derive(Default)] +pub struct InterningDefinedSymbol { + // Maps the symbol value to the constant DefId. + symbol_map: FxHashMap, +} + +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); + +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { + fn check_crate(&mut self, cx: &LateContext<'_>) { + if !self.symbol_map.is_empty() { + return; + } + + for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { + if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { + for item in cx.tcx.module_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + self.symbol_map.insert(value, item_def_id); + } + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(func, [arg]) = &expr.kind; + if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); + if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); + if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); + let value = Symbol::intern(&arg).as_u32(); + if let Some(&def_id) = self.symbol_map.get(&value); + then { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + cx.tcx.def_path_str(def_id), + Applicability::MachineApplicable, + ); + } + } + if let ExprKind::Binary(op, left, right) = expr.kind { + if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { + let data = [ + (left, self.symbol_str_expr(left, cx)), + (right, self.symbol_str_expr(right, cx)), + ]; + match data { + // both operands are a symbol string + [(_, Some(left)), (_, Some(right))] => { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary `Symbol` to string conversion", + "try", + format!( + "{} {} {}", + left.as_symbol_snippet(cx), + op.node.as_str(), + right.as_symbol_snippet(cx), + ), + Applicability::MachineApplicable, + ); + }, + // one of the operands is a symbol string + [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { + // creating an owned string for comparison + if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary string allocation", + "try", + format!("{}.as_str()", symbol.as_symbol_snippet(cx)), + Applicability::MachineApplicable, + ); + } + }, + // nothing found + [(_, None), (_, None)] => {}, + } + } + } + } +} + +impl InterningDefinedSymbol { + fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { + static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; + static SYMBOL_STR_PATHS: &[&[&str]] = &[ + &paths::SYMBOL_AS_STR, + &paths::SYMBOL_TO_IDENT_STRING, + &paths::TO_STRING_METHOD, + ]; + let call = if_chain! { + if let ExprKind::AddrOf(_, _, e) = expr.kind; + if let ExprKind::Unary(UnOp::Deref, e) = e.kind; + then { e } else { expr } + }; + if_chain! { + // is a method call + if let ExprKind::MethodCall(_, item, [], _) = call.kind; + if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); + let ty = cx.typeck_results().expr_ty(item); + // ...on either an Ident or a Symbol + if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + Some(false) + } else if match_type(cx, ty, &paths::IDENT) { + Some(true) + } else { + None + }; + // ...which converts it to a string + let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; + if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); + then { + let is_to_owned = path.last().unwrap().ends_with("string"); + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); + } + } + // is a string constant + if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + let value = Symbol::intern(&s).as_u32(); + // ...which matches a symbol constant + if let Some(&def_id) = self.symbol_map.get(&value) { + return Some(SymbolStrExpr::Const(def_id)); + } + } + None + } +} + +enum SymbolStrExpr<'tcx> { + /// a string constant with a corresponding symbol constant + Const(DefId), + /// a "symbol to string" expression like `symbol.as_str()` + Expr { + /// part that evaluates to `Symbol` or `Ident` + item: &'tcx Expr<'tcx>, + is_ident: bool, + /// whether an owned `String` is created like `to_ident_string()` + is_to_owned: bool, + }, +} + +impl<'tcx> SymbolStrExpr<'tcx> { + /// Returns a snippet that evaluates to a `Symbol` and is const if possible + fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { + match *self { + Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), + Self::Expr { item, is_ident, .. } => { + let mut snip = snippet(cx, item.span.source_callsite(), ".."); + if is_ident { + // get `Ident.name` + snip.to_mut().push_str(".name"); + } + snip + }, + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs new file mode 100644 index 000000000000..57eb3f49d062 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -0,0 +1,105 @@ +use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::def_path_res; +use clippy_utils::diagnostics::span_lint; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::Item; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, FloatTy}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks the paths module for invalid paths. + /// + /// ### Why is this bad? + /// It indicates a bug in the code. + /// + /// ### Example + /// None. + pub INVALID_PATHS, + internal, + "invalid path" +} + +// This is not a complete resolver for paths. It works on all the paths currently used in the paths +// module. That's all it does and all it needs to do. +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { + if def_path_res(cx, path, None) != Res::Err { + return true; + } + + // Some implementations can't be found by `path_to_res`, particularly inherent + // implementations of native types. Check lang items. + let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); + let lang_items = cx.tcx.lang_items(); + // This list isn't complete, but good enough for our current list of paths. + let incoherent_impls = [ + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), + SimplifiedTypeGen::SliceSimplifiedType, + SimplifiedTypeGen::StrSimplifiedType, + ] + .iter() + .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); + for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { + let lang_item_path = cx.get_def_path(*item_def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + if matches!( + cx.tcx.def_kind(*item_def_id), + DefKind::Mod | DefKind::Enum | DefKind::Trait + ) { + for child in cx.tcx.module_children(*item_def_id) { + if child.ident.name == *item { + return true; + } + } + } else { + for child in cx.tcx.associated_item_def_ids(*item_def_id) { + if cx.tcx.item_name(*child) == *item { + return true; + } + } + } + } + } + } + + false +} + +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidPaths { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let local_def_id = &cx.tcx.parent_module(item.hir_id()); + let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); + if_chain! { + if mod_name.as_str() == "paths"; + if let hir::ItemKind::Const(ty, body_id) = item.kind; + let ty = hir_ty_to_ty(cx.tcx, ty); + if let ty::Array(el_ty, _) = &ty.kind(); + if let ty::Ref(_, el_ty, _) = &el_ty.kind(); + if el_ty.is_str(); + let body = cx.tcx.hir().body(body_id); + let typeck_results = cx.tcx.typeck_body(body_id); + if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); + let path: Vec<&str> = path.iter().map(|x| { + if let Constant::Str(s) = x { + s.as_str() + } else { + // We checked the type of the constant above + unreachable!() + } + }).collect(); + if !check_path(cx, &path[..]); + then { + span_lint(cx, INVALID_PATHS, item.span, "invalid path"); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs new file mode 100644 index 000000000000..06dfc6e43607 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -0,0 +1,346 @@ +use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::{is_lint_allowed, match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast as ast; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::hir_id::CRATE_HIR_ID; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Ensures every lint is associated to a `LintPass`. + /// + /// ### Why is this bad? + /// The compiler only knows lints via a `LintPass`. Without + /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not + /// know the name of the lint. + /// + /// ### Known problems + /// Only checks for lints associated using the + /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. + /// + /// ### Example + /// ```rust,ignore + /// declare_lint! { pub LINT_1, ... } + /// declare_lint! { pub LINT_2, ... } + /// declare_lint! { pub FORGOTTEN_LINT, ... } + /// // ... + /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); + /// // missing FORGOTTEN_LINT + /// ``` + pub LINT_WITHOUT_LINT_PASS, + internal, + "declaring a lint without associating it in a LintPass" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases of an auto-generated lint without an updated description, + /// i.e. `default lint description`. + /// + /// ### Why is this bad? + /// Indicates that the lint is not finished. + /// + /// ### Example + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } + /// ``` + pub DEFAULT_LINT, + internal, + "found 'default lint description' in a lint declaration" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for invalid `clippy::version` attributes. + /// + /// Valid values are: + /// * "pre 1.29.0" + /// * any valid semantic version + pub INVALID_CLIPPY_VERSION_ATTRIBUTE, + internal, + "found an invalid `clippy::version` attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for declared clippy lints without the `clippy::version` attribute. + /// + pub MISSING_CLIPPY_VERSION_ATTRIBUTE, + internal, + "found clippy lint without `clippy::version` attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases of an auto-generated deprecated lint without an updated reason, + /// i.e. `"default deprecation note"`. + /// + /// ### Why is this bad? + /// Indicates that the documentation is incomplete. + /// + /// ### Example + /// ```rust,ignore + /// declare_deprecated_lint! { + /// /// ### What it does + /// /// Nothing. This lint has been deprecated. + /// /// + /// /// ### Deprecation reason + /// /// TODO + /// #[clippy::version = "1.63.0"] + /// pub COOL_LINT, + /// "default deprecation note" + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// declare_deprecated_lint! { + /// /// ### What it does + /// /// Nothing. This lint has been deprecated. + /// /// + /// /// ### Deprecation reason + /// /// This lint has been replaced by `cooler_lint` + /// #[clippy::version = "1.63.0"] + /// pub COOL_LINT, + /// "this lint has been replaced by `cooler_lint`" + /// } + /// ``` + pub DEFAULT_DEPRECATION_REASON, + internal, + "found 'default deprecation note' in a deprecated lint declaration" +} + +#[derive(Clone, Debug, Default)] +pub struct LintWithoutLintPass { + declared_lints: FxHashMap, + registered_lints: FxHashSet, +} + +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); + +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) + || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) + { + return; + } + + if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { + let is_lint_ref_ty = is_lint_ref_type(cx, ty); + if is_deprecated_lint(cx, ty) || is_lint_ref_ty { + check_invalid_clippy_version_attribute(cx, item); + + let expr = &cx.tcx.hir().body(body_id).value; + let fields; + if is_lint_ref_ty { + if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind + && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind { + fields = struct_fields; + } else { + return; + } + } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { + fields = struct_fields; + } else { + return; + } + + let field = fields + .iter() + .find(|f| f.ident.as_str() == "desc") + .expect("lints must have a description field"); + + if let ExprKind::Lit(Spanned { + node: LitKind::Str(ref sym, _), + .. + }) = field.expr.kind + { + let sym_str = sym.as_str(); + if is_lint_ref_ty { + if sym_str == "default lint description" { + span_lint( + cx, + DEFAULT_LINT, + item.span, + &format!("the lint `{}` has the default lint description", item.ident.name), + ); + } + + self.declared_lints.insert(item.ident.name, item.span); + } else if sym_str == "default deprecation note" { + span_lint( + cx, + DEFAULT_DEPRECATION_REASON, + item.span, + &format!("the lint `{}` has the default deprecation reason", item.ident.name), + ); + } + } + } + } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { + if !matches!( + cx.tcx.item_name(macro_call.def_id).as_str(), + "impl_lint_pass" | "declare_lint_pass" + ) { + return; + } + if let hir::ItemKind::Impl(hir::Impl { + of_trait: None, + items: impl_item_refs, + .. + }) = item.kind + { + let mut collector = LintCollector { + output: &mut self.registered_lints, + cx, + }; + let body_id = cx.tcx.hir().body_owned_by( + cx.tcx.hir().local_def_id( + impl_item_refs + .iter() + .find(|iiref| iiref.ident.as_str() == "get_lints") + .expect("LintPass needs to implement get_lints") + .id + .hir_id(), + ), + ); + collector.visit_expr(cx.tcx.hir().body(body_id).value); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) { + return; + } + + for (lint_name, &lint_span) in &self.declared_lints { + // When using the `declare_tool_lint!` macro, the original `lint_span`'s + // file points to "". + // `compiletest-rs` thinks that's an error in a different file and + // just ignores it. This causes the test in compile-fail/lint_pass + // not able to capture the error. + // Therefore, we need to climb the macro expansion tree and find the + // actual span that invoked `declare_tool_lint!`: + let lint_span = lint_span.ctxt().outer_expn_data().call_site; + + if !self.registered_lints.contains(lint_name) { + span_lint( + cx, + LINT_WITHOUT_LINT_PASS, + lint_span, + &format!("the lint `{lint_name}` is not added to any `LintPass`"), + ); + } + } + } +} + +pub(super) fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { + if let TyKind::Rptr( + _, + MutTy { + ty: inner, + mutbl: Mutability::Not, + }, + ) = ty.kind + { + if let TyKind::Path(ref path) = inner.kind { + if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { + return match_def_path(cx, def_id, &paths::LINT); + } + } + } + + false +} + +fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { + if let Some(value) = extract_clippy_version_value(cx, item) { + // The `sym!` macro doesn't work as it only expects a single token. + // It's better to keep it this way and have a direct `Symbol::intern` call here. + if value == Symbol::intern("pre 1.29.0") { + return; + } + + if RustcVersion::parse(value.as_str()).is_err() { + span_lint_and_help( + cx, + INVALID_CLIPPY_VERSION_ATTRIBUTE, + item.span, + "this item has an invalid `clippy::version` attribute", + None, + "please use a valid semantic version, see `doc/adding_lints.md`", + ); + } + } else { + span_lint_and_help( + cx, + MISSING_CLIPPY_VERSION_ATTRIBUTE, + item.span, + "this lint is missing the `clippy::version` attribute or version value", + None, + "please use a `clippy::version` attribute, see `doc/adding_lints.md`", + ); + } +} + +/// This function extracts the version value of a `clippy::version` attribute if the given value has +/// one +pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + attrs.iter().find_map(|attr| { + if_chain! { + // Identify attribute + if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; + if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; + if tool_name.ident.name == sym::clippy; + if attr_name.ident.name == sym::version; + if let Some(version) = attr.value_str(); + then { + Some(version) + } else { + None + } + } + }) +} + +struct LintCollector<'a, 'tcx> { + output: &'a mut FxHashSet, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { + type NestedFilter = nested_filter::All; + + fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { + if path.segments.len() == 1 { + self.output.insert(path.segments[0].ident.name); + } + } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } +} diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c84191bb0103..8efe170a1e5d 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,7 +8,7 @@ //! a simple mistake) use crate::renamed_lints::RENAMED_LINTS; -use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type}; +use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs new file mode 100644 index 000000000000..1e994e3f2b17 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::match_type; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::{self, subst::GenericArgKind}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. + /// + pub MISSING_MSRV_ATTR_IMPL, + internal, + "checking if all necessary steps were taken when adding a MSRV to a lint" +} + +declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); + +impl LateLintPass<'_> for MsrvAttrImpl { + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if_chain! { + if let hir::ItemKind::Impl(hir::Impl { + of_trait: Some(lint_pass_trait_ref), + self_ty, + items, + .. + }) = &item.kind; + if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); + let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); + if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); + let self_ty = hir_ty_to_ty(cx.tcx, self_ty); + if let ty::Adt(self_ty_def, _) = self_ty.kind(); + if self_ty_def.is_struct(); + if self_ty_def.all_fields().any(|f| { + cx.tcx + .type_of(f.did) + .walk() + .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) + .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) + }); + if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); + then { + let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; + let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; + let span = cx.sess().source_map().span_through_char(item.span, '{'); + span_lint_and_sugg( + cx, + MISSING_MSRV_ATTR_IMPL, + span, + &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), + &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), + format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs new file mode 100644 index 000000000000..3bc05d69579a --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs @@ -0,0 +1,62 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::match_type; +use clippy_utils::{is_lint_allowed, method_calls, paths}; +use if_chain::if_chain; +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_span::symbol::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `cx.outer().expn_data()` and suggests to use + /// the `cx.outer_expn_data()` + /// + /// ### Why is this bad? + /// `cx.outer_expn_data()` is faster and more concise. + /// + /// ### Example + /// ```rust,ignore + /// expr.span.ctxt().outer().expn_data() + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// expr.span.ctxt().outer_expn_data() + /// ``` + pub OUTER_EXPN_EXPN_DATA, + internal, + "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" +} + +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); + +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { + return; + } + + let (method_names, arg_lists, spans) = method_calls(expr, 2); + let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); + if_chain! { + if let ["expn_data", "outer_expn"] = method_names.as_slice(); + let (self_arg, args)= arg_lists[1]; + if args.is_empty(); + let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); + then { + span_lint_and_sugg( + cx, + OUTER_EXPN_EXPN_DATA, + spans[1].with_hi(expr.span.hi()), + "usage of `outer_expn().expn_data()`", + "try", + "outer_expn_data()".to_string(), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/produce_ice.rs b/clippy_lints/src/utils/internal_lints/produce_ice.rs new file mode 100644 index 000000000000..5899b94e16ba --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/produce_ice.rs @@ -0,0 +1,37 @@ +use rustc_ast::ast::NodeId; +use rustc_ast::visit::FnKind; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Not an actual lint. This lint is only meant for testing our customized internal compiler + /// error message by calling `panic`. + /// + /// ### Why is this bad? + /// ICE in large quantities can damage your teeth + /// + /// ### Example + /// ```rust,ignore + /// 🍦🍦🍦🍦🍦 + /// ``` + pub PRODUCE_ICE, + internal, + "this message should not appear anywhere as we ICE before and don't emit the lint" +} + +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); + +impl EarlyLintPass for ProduceIce { + fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { + assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); + } +} + +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { + match fn_kind { + FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", + FnKind::Closure(..) => false, + } +} diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs new file mode 100644 index 000000000000..9b524d5b07af --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -0,0 +1,260 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ExprKind, Local, Mutability, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; +use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::{Ident, Symbol}; + +use std::str; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. + /// + /// ### Why is this bad? + /// The path for an item is subject to change and is less efficient to look up than a + /// diagnostic item or a `LangItem`. + /// + /// ### Example + /// ```rust,ignore + /// utils::match_type(cx, ty, &paths::VEC) + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) + /// ``` + pub UNNECESSARY_DEF_PATH, + internal, + "using a def path when a diagnostic item or a `LangItem` is available" +} + +declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); + +#[allow(clippy::too_many_lines)] +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + enum Item { + LangItem(Symbol), + DiagnosticItem(Symbol), + } + static PATHS: &[&[&str]] = &[ + &["clippy_utils", "match_def_path"], + &["clippy_utils", "match_trait_method"], + &["clippy_utils", "ty", "match_type"], + &["clippy_utils", "is_expr_path_def_path"], + ]; + + if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind; + if let ExprKind::Path(path) = &func.kind; + if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if let Some(which_path) = match_any_def_paths(cx, id, PATHS); + let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; + // Extract the path to the matched type + if let Some(segments) = path_to_matched_type(cx, item_arg); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id(); + then { + // def_path_res will match field names before anything else, but for this we want to match + // inherent functions first. + let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field { + let method_name = *segments.last().unwrap(); + cx.tcx.def_key(def_id).parent + .and_then(|parent_idx| + cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter() + .find_map(|impl_id| cx.tcx.associated_items(*impl_id) + .find_by_name_and_kind( + cx.tcx, + Ident::from_str(method_name), + AssocKind::Fn, + *impl_id, + ) + ) + ) + .map_or(def_id, |item| item.def_id) + } else { + def_id + }; + + // Check if the target item is a diagnostic item or LangItem. + let (msg, item) = if let Some(item_name) + = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) + { + ( + "use of a def path to a diagnostic item", + Item::DiagnosticItem(*item_name), + ) + } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { + let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); + let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name; + ( + "use of a def path to a `LangItem`", + Item::LangItem(item_name), + ) + } else { + return; + }; + + let has_ctor = match cx.tcx.def_kind(def_id) { + DefKind::Struct => { + let variant = cx.tcx.adt_def(def_id).non_enum_variant(); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + } + DefKind::Variant => { + let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + } + _ => false, + }; + + let mut app = Applicability::MachineApplicable; + let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); + let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); + let (sugg, with_note) = match (which_path, item) { + // match_def_path + (0, Item::DiagnosticItem(item)) => + (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor), + (0, Item::LangItem(item)) => ( + format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), + has_ctor + ), + // match_trait_method + (1, Item::DiagnosticItem(item)) => + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false), + // match_type + (2, Item::DiagnosticItem(item)) => + (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), + (2, Item::LangItem(item)) => + (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false), + // is_expr_path_def_path + (3, Item::DiagnosticItem(item)) if has_ctor => ( + format!( + "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})", + ), + false, + ), + (3, Item::LangItem(item)) if has_ctor => ( + format!( + "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})", + ), + false, + ), + (3, Item::DiagnosticItem(item)) => + (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), + (3, Item::LangItem(item)) => ( + format!( + "path_res({cx_snip}, {def_snip}).opt_def_id()\ + .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", + ), + false, + ), + _ => return, + }; + + span_lint_and_then( + cx, + UNNECESSARY_DEF_PATH, + expr.span, + msg, + |diag| { + diag.span_suggestion(expr.span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead" + ); + } + }, + ); + } + } + } +} + +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + match peel_hir_expr_refs(expr).0.kind { + ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { + Res::Local(hir_id) => { + let parent_id = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { + path_to_matched_type(cx, init) + } else { + None + } + }, + Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( + cx, + cx.tcx.eval_static_initializer(def_id).ok()?.inner(), + cx.tcx.type_of(def_id), + ), + Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { + ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { + read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) + }, + _ => None, + }, + _ => None, + }, + ExprKind::Array(exprs) => exprs + .iter() + .map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some((*sym.as_str()).to_owned()); + } + } + + None + }) + .collect(), + _ => None, + } +} + +fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { + let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { + let &alloc = alloc.provenance().values().next()?; + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + (alloc.inner(), ty) + } else { + return None; + } + } else { + (alloc, ty) + }; + + if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() + && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() + && ty.is_str() + { + alloc + .provenance() + .values() + .map(|&alloc| { + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + let alloc = alloc.inner(); + str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) + .ok().map(ToOwned::to_owned) + } else { + None + } + }) + .collect() + } else { + None + } +} diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index a1b8e2ee162c..07c5941013c1 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,4 +1,4 @@ -thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs +thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic From c84ac4cee96f13b0abe8dc1f1b2d4e9406f80791 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 30 Sep 2022 21:10:10 -0400 Subject: [PATCH 030/524] Move some things around --- .../internal_lints/compiler_lint_functions.rs | 4 +- .../src/utils/internal_lints/invalid_paths.rs | 64 +++++++++---------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs index 67357a5cb6b0..d7e4a2c4422e 100644 --- a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs +++ b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs @@ -30,6 +30,8 @@ declare_clippy_lint! { "usage of the lint functions of the compiler instead of the utils::* variant" } +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); + #[derive(Clone, Default)] pub struct CompilerLintFunctions { map: FxHashMap<&'static str, &'static str>, @@ -48,8 +50,6 @@ impl CompilerLintFunctions { } } -impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); - impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) { diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 57eb3f49d062..04f1952e813a 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -25,6 +25,38 @@ declare_clippy_lint! { "invalid path" } +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidPaths { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let local_def_id = &cx.tcx.parent_module(item.hir_id()); + let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); + if_chain! { + if mod_name.as_str() == "paths"; + if let hir::ItemKind::Const(ty, body_id) = item.kind; + let ty = hir_ty_to_ty(cx.tcx, ty); + if let ty::Array(el_ty, _) = &ty.kind(); + if let ty::Ref(_, el_ty, _) = &el_ty.kind(); + if el_ty.is_str(); + let body = cx.tcx.hir().body(body_id); + let typeck_results = cx.tcx.typeck_body(body_id); + if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); + let path: Vec<&str> = path.iter().map(|x| { + if let Constant::Str(s) = x { + s.as_str() + } else { + // We checked the type of the constant above + unreachable!() + } + }).collect(); + if !check_path(cx, &path[..]); + then { + span_lint(cx, INVALID_PATHS, item.span, "invalid path"); + } + } + } +} + // This is not a complete resolver for paths. It works on all the paths currently used in the paths // module. That's all it does and all it needs to do. pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { @@ -71,35 +103,3 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { false } - -declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidPaths { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let local_def_id = &cx.tcx.parent_module(item.hir_id()); - let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if_chain! { - if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(ty, body_id) = item.kind; - let ty = hir_ty_to_ty(cx.tcx, ty); - if let ty::Array(el_ty, _) = &ty.kind(); - if let ty::Ref(_, el_ty, _) = &el_ty.kind(); - if el_ty.is_str(); - let body = cx.tcx.hir().body(body_id); - let typeck_results = cx.tcx.typeck_body(body_id); - if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - let path: Vec<&str> = path.iter().map(|x| { - if let Constant::Str(s) = x { - s.as_str() - } else { - // We checked the type of the constant above - unreachable!() - } - }).collect(); - if !check_path(cx, &path[..]); - then { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } - } - } -} From 8611a0bb5cc401f90162449eaa82295ef5f70d68 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 1 Oct 2022 04:48:01 -0400 Subject: [PATCH 031/524] Expand `unnecessary_def_path` lint --- clippy_lints/src/lib.rs | 2 +- .../internal_lints/unnecessary_def_path.rs | 181 +++++++++++++----- tests/ui-internal/unnecessary_def_path.fixed | 6 +- tests/ui-internal/unnecessary_def_path.rs | 6 +- .../unnecessary_def_path_hardcoded_path.rs | 16 ++ ...unnecessary_def_path_hardcoded_path.stderr | 27 +++ 6 files changed, 182 insertions(+), 56 deletions(-) create mode 100644 tests/ui-internal/unnecessary_def_path_hardcoded_path.rs create mode 100644 tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b2ee58ec7ff7..893410dbfdc9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -542,7 +542,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| { Box::::default() }); - store.register_late_pass(|_| Box::new(utils::internal_lints::unnecessary_def_path::UnnecessaryDefPath)); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass)); store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl)); } diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 9b524d5b07af..0a3852c98ee9 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -1,18 +1,20 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; use if_chain::if_chain; use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{ExprKind, Local, Mutability, Node}; +use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; use std::str; @@ -38,11 +40,56 @@ declare_clippy_lint! { "using a def path when a diagnostic item or a `LangItem` is available" } -declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); +impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); + +#[derive(Default)] +pub struct UnnecessaryDefPath { + array_def_ids: FxHashSet<(DefId, Span)>, + linted_def_ids: FxHashSet, +} -#[allow(clippy::too_many_lines)] impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { + return; + } + + match expr.kind { + ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span), + ExprKind::Array(elements) => self.check_array(cx, elements, expr.span), + _ => {}, + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for &(def_id, span) in &self.array_def_ids { + if self.linted_def_ids.contains(&def_id) { + continue; + } + + let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) { + ("diagnostic item", format!("sym::{sym}")) + } else if let Some(sym) = get_lang_item_name(cx, def_id) { + ("language item", format!("LangItem::{sym}")) + } else { + continue; + }; + + span_lint_and_help( + cx, + UNNECESSARY_DEF_PATH, + span, + &format!("hardcoded path to a {msg}"), + None, + &format!("convert all references to use `{sugg}`"), + ); + } + } +} + +impl UnnecessaryDefPath { + #[allow(clippy::too_many_lines)] + fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { enum Item { LangItem(Symbol), DiagnosticItem(Symbol), @@ -54,12 +101,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { &["clippy_utils", "is_expr_path_def_path"], ]; - if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { - return; - } - if_chain! { - if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind; + if let [cx_arg, def_arg, args@..] = args; if let ExprKind::Path(path) = &func.kind; if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); if let Some(which_path) = match_any_def_paths(cx, id, PATHS); @@ -67,29 +110,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { // Extract the path to the matched type if let Some(segments) = path_to_matched_type(cx, item_arg); let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id(); + if let Some(def_id) = inherent_def_path_res(cx, &segments[..]); then { - // def_path_res will match field names before anything else, but for this we want to match - // inherent functions first. - let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field { - let method_name = *segments.last().unwrap(); - cx.tcx.def_key(def_id).parent - .and_then(|parent_idx| - cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter() - .find_map(|impl_id| cx.tcx.associated_items(*impl_id) - .find_by_name_and_kind( - cx.tcx, - Ident::from_str(method_name), - AssocKind::Fn, - *impl_id, - ) - ) - ) - .map_or(def_id, |item| item.def_id) - } else { - def_id - }; - // Check if the target item is a diagnostic item or LangItem. let (msg, item) = if let Some(item_name) = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) @@ -98,9 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { "use of a def path to a diagnostic item", Item::DiagnosticItem(*item_name), ) - } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { - let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); - let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name; + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { ( "use of a def path to a `LangItem`", Item::LangItem(item_name), @@ -168,10 +188,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { span_lint_and_then( cx, UNNECESSARY_DEF_PATH, - expr.span, + span, msg, |diag| { - diag.span_suggestion(expr.span, "try", sugg, app); + diag.span_suggestion(span, "try", sugg, app); if with_note { diag.help( "if this `DefId` came from a constructor expression or pattern then the \ @@ -180,9 +200,19 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { } }, ); + + self.linted_def_ids.insert(def_id); } } } + + fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { + let Some(path) = path_from_array(elements) else { return }; + + if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::>()) { + self.array_def_ids.insert((def_id, span)); + } + } } fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { @@ -209,18 +239,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option None, }, - ExprKind::Array(exprs) => exprs - .iter() - .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some((*sym.as_str()).to_owned()); - } - } - - None - }) - .collect(), + ExprKind::Array(exprs) => path_from_array(exprs), _ => None, } } @@ -258,3 +277,67 @@ fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation None } } + +fn path_from_array(exprs: &[Expr<'_>]) -> Option> { + exprs + .iter() + .map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some((*sym.as_str()).to_owned()); + } + } + + None + }) + .collect() +} + +// def_path_res will match field names before anything else, but for this we want to match +// inherent functions first. +fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option { + def_path_res(cx, segments, None).opt_def_id().map(|def_id| { + if cx.tcx.def_kind(def_id) == DefKind::Field { + let method_name = *segments.last().unwrap(); + cx.tcx + .def_key(def_id) + .parent + .and_then(|parent_idx| { + cx.tcx + .inherent_impls(DefId { + index: parent_idx, + krate: def_id.krate, + }) + .iter() + .find_map(|impl_id| { + cx.tcx.associated_items(*impl_id).find_by_name_and_kind( + cx.tcx, + Ident::from_str(method_name), + AssocKind::Fn, + *impl_id, + ) + }) + }) + .map_or(def_id, |item| item.def_id) + } else { + def_id + } + }) +} + +fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option { + if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { + let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); + let item_name = cx + .tcx + .adt_def(lang_items) + .variants() + .iter() + .nth(lang_item) + .unwrap() + .name; + Some(item_name) + } else { + None + } +} diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index 4c050332f2cc..cbbb46523064 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -28,9 +28,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { @@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = is_type_diagnostic_item(cx, ty, sym::Result); let _ = is_type_diagnostic_item(cx, ty, sym::Result); - #[allow(unused)] + #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = is_type_diagnostic_item(cx, ty, sym::Rc); diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index 6506f1f164ac..f17fed6c6530 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -28,9 +28,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { @@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = match_type(cx, ty, RESULT); let _ = match_type(cx, ty, &["core", "result", "Result"]); - #[allow(unused)] + #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = clippy_utils::ty::match_type(cx, ty, rc_path); diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs new file mode 100644 index 000000000000..b5ff3a542056 --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs @@ -0,0 +1,16 @@ +#![feature(rustc_private)] +#![allow(unused)] +#![warn(clippy::unnecessary_def_path)] + +extern crate rustc_hir; + +use rustc_hir::LangItem; + +fn main() { + const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + + // Don't lint, not yet a diagnostic or language item + const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; +} diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr new file mode 100644 index 000000000000..af46d87bf676 --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -0,0 +1,27 @@ +error: hardcoded path to a language item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 + | +LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `LangItem::DerefMut` + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 + | +LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::deref_method` + +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 + | +LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::Deref` + +error: aborting due to 3 previous errors + From 2e5e3560e917f2c3adceda6bc40806868c98e04e Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 1 Oct 2022 04:56:55 -0400 Subject: [PATCH 032/524] Fix adjacent code --- clippy_lints/src/booleans.rs | 6 ++-- clippy_lints/src/comparison_chain.rs | 8 +++-- clippy_lints/src/functions/must_use.rs | 10 ++++--- clippy_lints/src/let_underscore.rs | 15 ++++++---- clippy_lints/src/loops/needless_range_loop.rs | 11 +++++-- clippy_lints/src/manual_async_fn.rs | 9 ++++-- clippy_lints/src/manual_clamp.rs | 10 +++++-- clippy_lints/src/matches/single_match.rs | 11 ++++--- clippy_lints/src/methods/mod.rs | 12 ++++---- .../src/methods/option_as_ref_deref.rs | 20 +++++++------ clippy_lints/src/methods/or_fun_call.rs | 18 +++++------ clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 4 +-- clippy_lints/src/operators/cmp_owned.rs | 18 ++++++----- clippy_lints/src/unit_return_expecting_ord.rs | 5 ++-- clippy_utils/src/lib.rs | 17 +++++++++++ clippy_utils/src/paths.rs | 30 ------------------- tests/ui-internal/invalid_paths.rs | 2 +- 17 files changed, 108 insertions(+), 98 deletions(-) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 2a15cbc7a3c3..08164c0b654e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::eq_expr_value; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{eq_expr_value, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -483,7 +483,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) + cx.tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) } struct NotSimplificationVisitor<'a, 'tcx> { diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index a05b41eb3ab5..0fe973b49a35 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq}; +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_span::sym; declare_clippy_lint! { /// ### What it does @@ -106,7 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { // Check that the type being compared implements `core::cmp::Ord` let ty = cx.typeck_results().expr_ty(lhs1); - let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])); + let is_ord = cx + .tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])); if !is_ord { return; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index d263804f32cf..3064b6c9d22f 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -7,14 +7,14 @@ use rustc_middle::{ lint::in_external_macro, ty::{self, Ty}, }; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, Symbol}; use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; +use clippy_utils::{return_ty, trait_ref_of_method}; use core::ops::ControlFlow; @@ -181,7 +181,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) } } -static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; +static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc]; fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool { match *ty.kind() { @@ -189,7 +189,9 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, ty::Adt(adt, substs) => { tys.insert(adt.did()) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) - || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did(), path)) + || KNOWN_WRAPPER_TYS + .iter() + .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did())) && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) }, ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)), diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 176787497ebf..6d50dcc88069 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_must_use_ty, match_type}; +use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type}; use clippy_utils::{is_must_use_func_call, paths}; use if_chain::if_chain; use rustc_hir::{Local, PatKind}; @@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Symbol}; declare_clippy_lint! { /// ### What it does @@ -99,10 +100,9 @@ declare_clippy_lint! { declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); -const SYNC_GUARD_PATHS: [&[&str]; 6] = [ - &paths::MUTEX_GUARD, - &paths::RWLOCK_READ_GUARD, - &paths::RWLOCK_WRITE_GUARD, +const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard]; + +const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::PARKING_LOT_MUTEX_GUARD, &paths::PARKING_LOT_RWLOCK_READ_GUARD, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD, @@ -121,7 +121,10 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => { - SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) + SYNC_GUARD_SYMS + .iter() + .any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym)) + || SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) }, GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 00cfc6d49f19..2b1c5688e820 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; +use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -302,8 +302,13 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if_chain! { // a range index op if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind; - if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) - || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); + if let Some(trait_id) = self + .cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .and_then(|def_id| self.cx.tcx.trait_of_item(def_id)); + if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) + || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)); if !self.check(args_1, args_0, expr); then { return } } diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 9a0a26c0991b..570d83c7ea82 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_function_call; -use clippy_utils::paths::FUTURE_FROM_GENERATOR; +use clippy_utils::match_function_call_with_def_id; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -175,7 +174,11 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { if_chain! { if let Some(block_expr) = block.expr; - if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR); + if let Some(args) = cx + .tcx + .lang_items() + .from_generator_fn() + .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id)); if args.len() == 1; if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0]; let closure_body = cx.tcx.hir().body(body); diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index ece4df95505c..02dc8755dd61 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -12,9 +12,9 @@ use std::ops::Deref; use clippy_utils::{ diagnostics::{span_lint_and_then, span_lint_hir_and_then}, - eq_expr_value, get_trait_def_id, + eq_expr_value, higher::If, - is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, paths, peel_blocks, + is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, sugg::Sugg, ty::implements_trait, @@ -190,7 +190,11 @@ impl TypeClampability { fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option { if ty.is_floating_point() { Some(TypeClampability::Float) - } else if get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) { + } else if cx + .tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { Some(TypeClampability::Ord) } else { None diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index d496107ffd6b..e5a15b2e1a1d 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{expr_block, snippet}; -use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs}; -use clippy_utils::{ - is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, -}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs}; +use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; use core::cmp::max; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -156,10 +155,10 @@ fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> /// Returns `true` if the given type is an enum we know won't be expanded in the future fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { // list of candidate `Enum`s we know will never get any more members - let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT]; + let candidates = [sym::Cow, sym::Option, sym::Result]; for candidate_ty in candidates { - if match_type(cx, ty, candidate_ty) { + if is_type_diagnostic_item(cx, ty, candidate_ty) { return true; } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 78c1b33ed97b..b0677d1a3ae3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -102,9 +102,7 @@ use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{ - contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty, -}; +use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; @@ -3846,12 +3844,12 @@ impl SelfKind { return m == mutability && t == parent_ty; } - let trait_path = match mutability { - hir::Mutability::Not => &paths::ASREF_TRAIT, - hir::Mutability::Mut => &paths::ASMUT_TRAIT, + let trait_sym = match mutability { + hir::Mutability::Not => sym::AsRef, + hir::Mutability::Mut => sym::AsMut, }; - let Some(trait_def_id) = get_trait_def_id(cx, trait_path) else { + let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { return false }; implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 6fb92d1c663c..742483e6b2e5 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -32,8 +32,7 @@ pub(super) fn check<'tcx>( return; } - let deref_aliases: [&[&str]; 9] = [ - &paths::DEREF_TRAIT_METHOD, + let deref_aliases: [&[&str]; 8] = [ &paths::DEREF_MUT_TRAIT_METHOD, &paths::CSTRING_AS_C_STR, &paths::OS_STRING_AS_OS_STR, @@ -45,12 +44,14 @@ pub(super) fn check<'tcx>( ]; let is_deref = match map_arg.kind { - hir::ExprKind::Path(ref expr_qpath) => cx - .qpath_res(expr_qpath, map_arg.hir_id) - .opt_def_id() - .map_or(false, |fun_def_id| { - deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) - }), + hir::ExprKind::Path(ref expr_qpath) => { + cx.qpath_res(expr_qpath, map_arg.hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) + || deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }) + }, hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(closure_body.value); @@ -68,7 +69,8 @@ pub(super) fn check<'tcx>( if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; then { let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + cx.tcx.is_diagnostic_item(sym::deref_method, method_did) + || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) } else { false } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 6a35024d0361..6e10445659ef 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; -use clippy_utils::ty::{implements_trait, match_type}; -use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{contains_return, is_trait_item, last_path_segment}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Symbol}; use std::borrow::Cow; use super::OR_FUN_CALL; @@ -88,11 +88,11 @@ pub(super) fn check<'tcx>( fun_span: Option, ) { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ - (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (&paths::RESULT, true, &["or", "unwrap_or"], "else"), + const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ + (sym::BTreeEntry, false, &["or_insert"], "with"), + (sym::HashMapEntry, false, &["or_insert"], "with"), + (sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), + (sym::Result, true, &["or", "unwrap_or"], "else"), ]; if_chain! { @@ -104,7 +104,7 @@ pub(super) fn check<'tcx>( let self_ty = cx.typeck_results().expr_ty(self_expr); if let Some(&(_, fn_has_arguments, poss, suffix)) = - KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0)); + KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0)); if poss.contains(&name); diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index a7e0e35787cf..c949cede8e10 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::implements_trait; -use clippy_utils::{self, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { let ty = cx.typeck_results().expr_ty(left); let implements_ord = { - if let Some(id) = get_trait_def_id(cx, &paths::ORD) { + if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) { implements_trait(cx, ty, id, &[]) } else { return; diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index c9c777f1bd8d..24aeb82a37f3 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{match_any_def_paths, path_def_id, paths}; +use clippy_utils::{match_def_path, path_def_id, paths}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -49,13 +49,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path) - .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) - .map_or(false, |idx| match idx { - 0 => true, - 1 => !is_copy(cx, typeck.expr_ty(expr)), - _ => false, - }) => + if path_def_id(cx, path).map_or(false, |id| { + if match_def_path(cx, id, &paths::FROM_STR_METHOD) { + true + } else if cx.tcx.lang_items().from_fn() == Some(id) { + !is_copy(cx, typeck.expr_ty(expr)) + } else { + false + } + }) => { (arg, arg.span) }, diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 57aff5367dd1..fc2ee9e5a4db 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::{get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; @@ -7,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{BytePos, Span}; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -80,7 +79,7 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve let fn_sig = cx.tcx.fn_sig(def_id); let generics = cx.tcx.predicates_of(def_id); let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait()); - let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD)); + let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord)); let partial_ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait()); // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index dbe75b43cb24..cbc2fde69d12 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1766,6 +1766,7 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool /// ```rust,ignore /// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX); /// ``` +/// This function is deprecated. Use [`match_function_call_with_def_id`]. pub fn match_function_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -1783,6 +1784,22 @@ pub fn match_function_call<'tcx>( None } +pub fn match_function_call_with_def_id<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + fun_def_id: DefId, +) -> Option<&'tcx [Expr<'tcx>]> { + if_chain! { + if let ExprKind::Call(fun, args) = expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id); + then { + return Some(args); + } + }; + None +} + /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if /// any. /// diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 78adc4536543..bc8514734304 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -16,25 +16,17 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ #[cfg(feature = "internal")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; -pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; -pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; -pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; -pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; -pub const CORE_ITER_INTO_ITER: [&str; 6] = ["core", "iter", "traits", "collect", "IntoIterator", "into_iter"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; -/// Preferably use the diagnostic item `sym::deref_method` where possible -pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; -pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; #[cfg(feature = "internal")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; #[cfg(feature = "internal")] @@ -42,30 +34,22 @@ pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"] pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; -pub const FILE: [&str; 3] = ["std", "fs", "File"]; -pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; -pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; -pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; -pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"]; #[cfg(feature = "internal")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; #[cfg(feature = "internal")] pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; -pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; -pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; -pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -76,13 +60,7 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; #[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"]; -pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; -/// Preferably use the diagnostic item `sym::Option` where possible -pub const OPTION: [&str; 3] = ["core", "option", "Option"]; -pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"]; -pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"]; -pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; @@ -95,8 +73,6 @@ pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; #[cfg_attr(not(unix), allow(clippy::invalid_paths))] pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; -pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; -pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"]; pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; @@ -125,14 +101,8 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; -/// Preferably use the diagnostic item `sym::Result` where possible -pub const RESULT: [&str; 3] = ["core", "result", "Result"]; -pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; -pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; #[cfg(feature = "internal")] pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"]; -pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"]; -pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs index b823ff7fe37f..9a9790a4bae5 100644 --- a/tests/ui-internal/invalid_paths.rs +++ b/tests/ui-internal/invalid_paths.rs @@ -1,5 +1,5 @@ #![warn(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)] mod paths { // Good path From 5dc54c60660b2e37c2978c38df9298edcc2988f2 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 9 Oct 2022 07:01:49 -0400 Subject: [PATCH 033/524] Format affected files --- clippy_lints/src/let_underscore.rs | 8 +-- clippy_lints/src/loops/needless_range_loop.rs | 24 ++++--- clippy_lints/src/manual_async_fn.rs | 9 ++- clippy_lints/src/methods/mod.rs | 10 ++- clippy_lints/src/methods/or_fun_call.rs | 5 +- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 4 +- clippy_lints/src/unit_return_expecting_ord.rs | 12 ++-- .../utils/internal_lints/collapsible_calls.rs | 13 ++-- .../internal_lints/compiler_lint_functions.rs | 3 +- .../utils/internal_lints/if_chain_style.rs | 5 +- .../src/utils/internal_lints/invalid_paths.rs | 7 +- .../internal_lints/lint_without_lint_pass.rs | 6 +- .../internal_lints/metadata_collector.rs | 6 +- .../internal_lints/outer_expn_data_pass.rs | 2 +- .../internal_lints/unnecessary_def_path.rs | 70 +++++++++---------- 15 files changed, 104 insertions(+), 80 deletions(-) diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 6d50dcc88069..b7798b1c1d74 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "non-binding let on a synchronization lock", None, "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`" + binding or dropping explicitly with `std::mem::drop`", ); } else if init_ty.needs_drop(cx.tcx, cx.param_env) { span_lint_and_help( @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "non-binding `let` on a type that implements `Drop`", None, "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`" + binding or dropping explicitly with `std::mem::drop`", ); } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { span_lint_and_help( @@ -156,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { local.span, "non-binding let on an expression with `#[must_use]` type", None, - "consider explicitly using expression value" + "consider explicitly using expression value", ); } else if is_must_use_func_call(cx, init) { span_lint_and_help( @@ -165,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { local.span, "non-binding let on a result of a `#[must_use]` function", None, - "consider explicitly using function result" + "consider explicitly using function result", ); } } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 2b1c5688e820..27ba27202bf7 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -263,7 +263,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { match res { Res::Local(hir_id) => { let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let extent = self.cx + let extent = self + .cx .tcx .region_scope_tree(parent_def_id) .var_scope(hir_id.local_id) @@ -274,11 +275,12 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), ); } else { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); + self.indexed_indirectly + .insert(seqvar.segments[0].ident.name, Some(extent)); } - return false; // no need to walk further *on the variable* - } - Res::Def(DefKind::Static (_)| DefKind::Const, ..) => { + return false; // no need to walk further *on the variable* + }, + Res::Def(DefKind::Static(_) | DefKind::Const, ..) => { if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, @@ -287,8 +289,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } else { self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); } - return false; // no need to walk further *on the variable* - } + return false; // no need to walk further *on the variable* + }, _ => (), } } @@ -310,14 +312,18 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)); if !self.check(args_1, args_0, expr); - then { return } + then { + return; + } } if_chain! { // an index op if let ExprKind::Index(seqexpr, idx) = expr.kind; if !self.check(idx, seqexpr, expr); - then { return } + then { + return; + } } if_chain! { diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 570d83c7ea82..090f9f8ff73c 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -139,9 +139,9 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t if args.bindings.len() == 1; let binding = &args.bindings[0]; if binding.ident.name == sym::Output; - if let TypeBindingKind::Equality{term: Term::Ty(output)} = binding.kind; + if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind; then { - return Some(output) + return Some(output); } } @@ -180,7 +180,10 @@ fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) .from_generator_fn() .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id)); if args.len() == 1; - if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0]; + if let Expr { + kind: ExprKind::Closure(&Closure { body, .. }), + .. + } = args[0]; let closure_body = cx.tcx.hir().body(body); if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block)); then { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b0677d1a3ae3..fb92779be2a7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3370,7 +3370,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { let first_arg_span = first_arg_ty.span; let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); + let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()) + .self_ty() + .skip_binder(); wrong_self_convention::check( cx, item.ident.name.as_str(), @@ -3378,7 +3380,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { first_arg_ty, first_arg_span, false, - true + true, ); } } @@ -3387,7 +3389,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id()); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); + let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()) + .self_ty() + .skip_binder(); if !ret_ty.contains(self_ty); then { diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 6e10445659ef..991d3dd538bf 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -121,10 +121,9 @@ pub(super) fn check<'tcx>( macro_expanded_snipped = snippet(cx, snippet_span, ".."); match macro_expanded_snipped.strip_prefix("$crate::vec::") { Some(stripped) => Cow::from(stripped), - None => macro_expanded_snipped + None => macro_expanded_snipped, } - } - else { + } else { not_macro_argument_snippet } }; diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index c949cede8e10..5c2b96f5b2ce 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -47,14 +47,12 @@ declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]); impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if !in_external_macro(cx.sess(), expr.span); if let ExprKind::Unary(UnOp::Not, inner) = expr.kind; if let ExprKind::Binary(ref op, left, _) = inner.kind; if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node; then { - let ty = cx.typeck_results().expr_ty(left); let implements_ord = { @@ -81,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { "the use of negated comparison operators on partially ordered \ types produces code that is hard to read and refactor, please \ consider using the `partial_cmp` method instead, to make it \ - clear that the two values could be incomparable" + clear that the two values could be incomparable", ); } } diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index fc2ee9e5a4db..1307288623f9 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -98,11 +98,15 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve if trait_pred.self_ty() == inp; if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); then { - if ord_preds.iter().any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) { + if ord_preds + .iter() + .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) + { args_to_check.push((i, "Ord".to_string())); - } else if partial_ord_preds.iter().any(|pord| { - pord.self_ty() == return_ty_pred.term.ty().unwrap() - }) { + } else if partial_ord_preds + .iter() + .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) + { args_to_check.push((i, "PartialOrd".to_string())); } } diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs index c9089aecfa59..d7666b77f6e9 100644 --- a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -92,7 +92,12 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { let mut sle = SpanlessEq::new(cx).deny_side_effects(); match ps.ident.as_str() { "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); + suggest_suggestion( + cx, + expr, + &and_then_snippets, + &span_suggestion_snippets(cx, span_call_args), + ); }, "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); @@ -105,12 +110,12 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { "help" => { let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); - } + }, "note" => { let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); - } - _ => (), + }, + _ => (), } } } diff --git a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs index d7e4a2c4422e..cacd05262a21 100644 --- a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs +++ b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs @@ -61,8 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { let fn_name = path.ident; if let Some(sugg) = self.map.get(fn_name.as_str()); let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, ty, &paths::EARLY_CONTEXT) - || match_type(cx, ty, &paths::LATE_CONTEXT); + if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); then { span_lint_and_help( cx, diff --git a/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/clippy_lints/src/utils/internal_lints/if_chain_style.rs index a863fdc9d50c..883a5c08e5c1 100644 --- a/clippy_lints/src/utils/internal_lints/if_chain_style.rs +++ b/clippy_lints/src/utils/internal_lints/if_chain_style.rs @@ -94,7 +94,10 @@ fn check_nested_if_chains( .iter() .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); - then {} else { return } + then { + } else { + return; + } } let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 04f1952e813a..25532dd4e268 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -41,14 +41,17 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { let body = cx.tcx.hir().body(body_id); let typeck_results = cx.tcx.typeck_body(body_id); if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - let path: Vec<&str> = path.iter().map(|x| { + let path: Vec<&str> = path + .iter() + .map(|x| { if let Constant::Str(s) = x { s.as_str() } else { // We checked the type of the constant above unreachable!() } - }).collect(); + }) + .collect(); if !check_path(cx, &path[..]); then { span_lint(cx, INVALID_PATHS, item.span, "invalid path"); diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 06dfc6e43607..0dac64376b06 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -317,11 +317,7 @@ pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item< if tool_name.ident.name == sym::clippy; if attr_name.ident.name == sym::version; if let Some(version) = attr.value_str(); - then { - Some(version) - } else { - None - } + then { Some(version) } else { None } } }) } diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 8efe170a1e5d..d06a616e4b30 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -532,7 +532,11 @@ fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec, String)> { // Extract lints doc_comment.make_ascii_lowercase(); - let lints: Vec = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect(); + let lints: Vec = doc_comment + .split_off(DOC_START.len()) + .split(", ") + .map(str::to_string) + .collect(); // Format documentation correctly // split off leading `.` from lint name list and indent for correct formatting diff --git a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs index 3bc05d69579a..2b13fad80665 100644 --- a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs +++ b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs @@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); if_chain! { if let ["expn_data", "outer_expn"] = method_names.as_slice(); - let (self_arg, args)= arg_lists[1]; + let (self_arg, args) = arg_lists[1]; if args.is_empty(); let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 0a3852c98ee9..4cf76f536255 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -102,7 +102,7 @@ impl UnnecessaryDefPath { ]; if_chain! { - if let [cx_arg, def_arg, args@..] = args; + if let [cx_arg, def_arg, args @ ..] = args; if let ExprKind::Path(path) = &func.kind; if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); if let Some(which_path) = match_any_def_paths(cx, id, PATHS); @@ -113,6 +113,7 @@ impl UnnecessaryDefPath { if let Some(def_id) = inherent_def_path_res(cx, &segments[..]); then { // Check if the target item is a diagnostic item or LangItem. + #[rustfmt::skip] let (msg, item) = if let Some(item_name) = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) { @@ -133,11 +134,11 @@ impl UnnecessaryDefPath { DefKind::Struct => { let variant = cx.tcx.adt_def(def_id).non_enum_variant(); variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } + }, DefKind::Variant => { let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } + }, _ => false, }; @@ -146,35 +147,40 @@ impl UnnecessaryDefPath { let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); let (sugg, with_note) = match (which_path, item) { // match_def_path - (0, Item::DiagnosticItem(item)) => - (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor), + (0, Item::DiagnosticItem(item)) => ( + format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), + has_ctor, + ), (0, Item::LangItem(item)) => ( format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), - has_ctor + has_ctor, ), // match_trait_method - (1, Item::DiagnosticItem(item)) => - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false), + (1, Item::DiagnosticItem(item)) => { + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) + }, // match_type - (2, Item::DiagnosticItem(item)) => - (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (2, Item::LangItem(item)) => - (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false), + (2, Item::DiagnosticItem(item)) => ( + format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (2, Item::LangItem(item)) => ( + format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), + false, + ), // is_expr_path_def_path (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!( - "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})", - ), + format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), false, ), (3, Item::LangItem(item)) if has_ctor => ( - format!( - "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})", - ), + format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), + false, + ), + (3, Item::DiagnosticItem(item)) => ( + format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false, ), - (3, Item::DiagnosticItem(item)) => - (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), (3, Item::LangItem(item)) => ( format!( "path_res({cx_snip}, {def_snip}).opt_def_id()\ @@ -185,21 +191,15 @@ impl UnnecessaryDefPath { _ => return, }; - span_lint_and_then( - cx, - UNNECESSARY_DEF_PATH, - span, - msg, - |diag| { - diag.span_suggestion(span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead" - ); - } - }, - ); + span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { + diag.span_suggestion(span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead", + ); + } + }); self.linted_def_ids.insert(def_id); } From d38175f27188274bf5d6c1e433907bc50281c616 Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 15 Oct 2022 14:57:08 +0200 Subject: [PATCH 034/524] `explicit_ty_bound` code golf --- clippy_lints/src/default_numeric_fallback.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 199f8e10e549..03460689e19a 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -88,10 +88,9 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { if_chain! { if !in_external_macro(self.cx.sess(), lit.span); - if let Some(explicit_ty_bounds) = self.ty_bounds.last(); + if matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - if !explicit_ty_bounds.0; then { let (suffix, is_float) = match lit_ty.kind() { ty::Int(IntTy::I32) => ("i32", false), From 2e3342af4a00eb7677e3b7a1d110d773226fc43f Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 15 Oct 2022 15:10:50 +0200 Subject: [PATCH 035/524] [`zero_prefixed_literal`] Do not advise to use octal form if not possible --- .../src/misc_early/zero_prefixed_literal.rs | 18 ++++++---- tests/ui/literals.rs | 7 ++++ tests/ui/literals.stderr | 35 ++++++++++++++++++- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 4963bba82f2d..9ead43ea4a47 100644 --- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -6,6 +6,7 @@ use rustc_lint::EarlyContext; use super::ZERO_PREFIXED_LITERAL; pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { + let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0'); span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, @@ -15,15 +16,18 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { diag.span_suggestion( lit.span, "if you mean to use a decimal constant, remove the `0` to avoid confusion", - lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - lit.span, - "if you mean to use an octal constant, use `0o`", - format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), + trimmed_lit_snip.to_string(), Applicability::MaybeIncorrect, ); + // do not advise to use octal form if the literal cannot be expressed in base 8. + if !lit_snip.contains(|c| c == '8' || c == '9') { + diag.span_suggestion( + lit.span, + "if you mean to use an octal constant, use `0o`", + format!("0o{trimmed_lit_snip}"), + Applicability::MaybeIncorrect, + ); + } }, ); } diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index 0cadd5a3da19..1a646e49ce3a 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -40,3 +40,10 @@ fn main() { let ok26 = 0x6_A0_BF; let ok27 = 0b1_0010_0101; } + +fn issue9651() { + // lint but octal form is not possible here + let _ = 08; + let _ = 09; + let _ = 089; +} diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 365b24074735..603d47bacca8 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -135,5 +135,38 @@ error: digits of hex or binary literal not grouped by four LL | let fail25 = 0b01_100_101; | ^^^^^^^^^^^^ help: consider: `0b0110_0101` -error: aborting due to 18 previous errors +error: this is a decimal constant + --> $DIR/literals.rs:46:13 + | +LL | let _ = 08; + | ^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 8; + | ~ + +error: this is a decimal constant + --> $DIR/literals.rs:47:13 + | +LL | let _ = 09; + | ^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 9; + | ~ + +error: this is a decimal constant + --> $DIR/literals.rs:48:13 + | +LL | let _ = 089; + | ^^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 89; + | ~~ + +error: aborting due to 21 previous errors From 1450710f12f74ce6bd55c44e4170fbd352846f3d Mon Sep 17 00:00:00 2001 From: alex-semenyuk Date: Sat, 15 Oct 2022 23:52:40 +0300 Subject: [PATCH 036/524] Enable test no_std_main_recursion --- tests/ui/crate_level_checks/no_std_main_recursion.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ui/crate_level_checks/no_std_main_recursion.rs b/tests/ui/crate_level_checks/no_std_main_recursion.rs index 4a5c597dda51..e1c9fe30a9df 100644 --- a/tests/ui/crate_level_checks/no_std_main_recursion.rs +++ b/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -1,6 +1,5 @@ // compile-flags: -Clink-arg=-nostartfiles // ignore-macos -// ignore-windows #![feature(lang_items, start, libc)] #![no_std] From f8ae2f580774c0d7743c5c6adf8e246735a28c92 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sat, 15 Oct 2022 23:19:43 +0200 Subject: [PATCH 037/524] fix `box-default` linting `no_std` non-boxes --- clippy_lints/src/box_default.rs | 2 +- tests/ui/box_default_no_std.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 tests/ui/box_default_no_std.rs diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index f35a79dcc739..bb0307e8856d 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -46,7 +46,7 @@ impl LateLintPass<'_> for BoxDefault { && !in_external_macro(cx.sess(), expr.span) && (expr.span.eq_ctxt(arg.span) || is_vec_expn(cx, arg)) && seg.ident.name == sym::new - && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box() + && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) && is_default_equivalent(cx, arg) { let arg_ty = cx.typeck_results().expr_ty(arg); diff --git a/tests/ui/box_default_no_std.rs b/tests/ui/box_default_no_std.rs new file mode 100644 index 000000000000..4326abc9a541 --- /dev/null +++ b/tests/ui/box_default_no_std.rs @@ -0,0 +1,33 @@ +#![feature(lang_items, start, libc)] +#![warn(clippy::box_default)] +#![no_std] + +pub struct NotBox { + _value: T, +} + +impl NotBox { + pub fn new(value: T) -> Self { + Self { _value: value } + } +} + +impl Default for NotBox { + fn default() -> Self { + Self::new(T::default()) + } +} + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _p = NotBox::new(isize::default()); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} From 7ac97b69fc81fcd5cbd2a7c862187f6a6c6ea354 Mon Sep 17 00:00:00 2001 From: TennyZhuang Date: Sun, 16 Oct 2022 16:02:23 +0800 Subject: [PATCH 038/524] Add new lint `partial_pub_fields` Signed-off-by: TennyZhuang --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_restriction.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/partial_pub_fields.rs | 81 ++++++++++++++++++++ src/docs.rs | 1 + src/docs/partial_pub_fields.txt | 27 +++++++ tests/ui/partial_pub_fields.rs | 20 +++++ tests/ui/partial_pub_fields.stderr | 19 +++++ 9 files changed, 153 insertions(+) create mode 100644 clippy_lints/src/partial_pub_fields.rs create mode 100644 src/docs/partial_pub_fields.txt create mode 100644 tests/ui/partial_pub_fields.rs create mode 100644 tests/ui/partial_pub_fields.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index f593966c0459..1e0ff5db0ee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4134,6 +4134,7 @@ Released 2018-09-13 [`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap +[`partial_pub_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#partial_pub_fields [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index de1253c8510a..799c62743aaa 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -479,6 +479,7 @@ store.register_lints(&[ panic_unimplemented::TODO, panic_unimplemented::UNIMPLEMENTED, panic_unimplemented::UNREACHABLE, + partial_pub_fields::PARTIAL_PUB_FIELDS, partialeq_ne_impl::PARTIALEQ_NE_IMPL, partialeq_to_none::PARTIALEQ_TO_NONE, pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 6eb9b3d3b9b7..9edced28408f 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -61,6 +61,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(panic_unimplemented::TODO), LintId::of(panic_unimplemented::UNIMPLEMENTED), LintId::of(panic_unimplemented::UNREACHABLE), + LintId::of(partial_pub_fields::PARTIAL_PUB_FIELDS), LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), LintId::of(pub_use::PUB_USE), LintId::of(redundant_slicing::DEREF_BY_SLICING), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ebb0f14fef52..bf5688829e22 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -324,6 +324,7 @@ mod option_if_let_else; mod overflow_check_conditional; mod panic_in_result_fn; mod panic_unimplemented; +mod partial_pub_fields; mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; @@ -908,6 +909,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)); store.register_late_pass(|_| Box::new(box_default::BoxDefault)); store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); + store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs new file mode 100644 index 000000000000..085ee08afca9 --- /dev/null +++ b/clippy_lints/src/partial_pub_fields.rs @@ -0,0 +1,81 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::*; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks whether partial fields of a struct are public. + /// + /// Either make all fields of a type public, or make none of them public + /// + /// ### Why is this bad? + /// Most types should either be: + /// * Abstract data types: complex objects with opaque implementation which guard + /// interior invariants and expose intentionally limited API to the outside world. + /// * Data: relatively simple objects which group a bunch of related attributes together. + /// + /// ### Example + /// ```rust + /// pub struct Color { + /// pub r, + /// pub g, + /// b, + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub struct Color { + /// pub r, + /// pub g, + /// pub b, + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub PARTIAL_PUB_FIELDS, + restriction, + "partial fields of a struct are public" +} +declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]); + +impl EarlyLintPass for PartialPubFields { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + let ItemKind::Struct(ref st, _) = item.kind else { + return; + }; + + let mut fields = st.fields().iter(); + let Some(first_field) = fields.next() else { + // Empty struct. + return; + }; + let all_pub = first_field.vis.kind.is_pub(); + let all_priv = !all_pub; + + let msg = "mixed usage of pub and non-pub fields"; + + for field in fields { + if all_priv && field.vis.kind.is_pub() { + span_lint_and_help( + cx, + &PARTIAL_PUB_FIELDS, + field.vis.span, + msg, + None, + "consider using private field here", + ); + return; + } else if all_pub && !field.vis.kind.is_pub() { + span_lint_and_help( + cx, + &PARTIAL_PUB_FIELDS, + field.vis.span, + msg, + None, + "consider using public field here", + ); + return; + } + } + } +} diff --git a/src/docs.rs b/src/docs.rs index 41c31f91bca5..b8b4286b488a 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -395,6 +395,7 @@ docs! { "panic", "panic_in_result_fn", "panicking_unwrap", + "partial_pub_fields", "partialeq_ne_impl", "partialeq_to_none", "path_buf_push_overwrite", diff --git a/src/docs/partial_pub_fields.txt b/src/docs/partial_pub_fields.txt new file mode 100644 index 000000000000..a332ec8c28a6 --- /dev/null +++ b/src/docs/partial_pub_fields.txt @@ -0,0 +1,27 @@ +### What it does +Checks whether partial fields of a struct are public. + +Either make all fields of a type public, or make none of them public + +### Why is this bad? +Most types should either be: +* Abstract data types: complex objects with opaque implementation which guard +interior invariants and expose intentionally limited API to the outside world. +* Data: relatively simple objects which group a bunch of related attributes together. + +### Example +``` +pub struct Color { + pub r, + pub g, + b, +} +``` +Use instead: +``` +pub struct Color { + pub r, + pub g, + pub b, +} +``` \ No newline at end of file diff --git a/tests/ui/partial_pub_fields.rs b/tests/ui/partial_pub_fields.rs new file mode 100644 index 000000000000..e1a4b4827991 --- /dev/null +++ b/tests/ui/partial_pub_fields.rs @@ -0,0 +1,20 @@ +#![allow(unused)] +#![warn(clippy::partial_pub_fields)] + +fn main() { + // test code goes here + + use std::collections::HashMap; + + #[derive(Default)] + pub struct FileSet { + files: HashMap, + pub paths: HashMap, + } + + pub struct Color { + pub r: u8, + pub g: u8, + b: u8, + } +} diff --git a/tests/ui/partial_pub_fields.stderr b/tests/ui/partial_pub_fields.stderr new file mode 100644 index 000000000000..f7f23c85a67e --- /dev/null +++ b/tests/ui/partial_pub_fields.stderr @@ -0,0 +1,19 @@ +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:12:9 + | +LL | pub paths: HashMap, + | ^^^ + | + = help: consider using private field here + = note: `-D clippy::partial-pub-fields` implied by `-D warnings` + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:18:9 + | +LL | b: u8, + | ^ + | + = help: consider using public field here + +error: aborting due to 2 previous errors + From b10882ab9153733249f95d63150724aa5f50ce59 Mon Sep 17 00:00:00 2001 From: TennyZhuang Date: Sun, 16 Oct 2022 16:21:48 +0800 Subject: [PATCH 039/524] fix dogfood Signed-off-by: TennyZhuang --- clippy_lints/src/partial_pub_fields.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs index 085ee08afca9..42f892e3652a 100644 --- a/clippy_lints/src/partial_pub_fields.rs +++ b/clippy_lints/src/partial_pub_fields.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_ast::ast::*; +use rustc_ast::ast::{Item, ItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -58,7 +58,7 @@ impl EarlyLintPass for PartialPubFields { if all_priv && field.vis.kind.is_pub() { span_lint_and_help( cx, - &PARTIAL_PUB_FIELDS, + PARTIAL_PUB_FIELDS, field.vis.span, msg, None, @@ -68,7 +68,7 @@ impl EarlyLintPass for PartialPubFields { } else if all_pub && !field.vis.kind.is_pub() { span_lint_and_help( cx, - &PARTIAL_PUB_FIELDS, + PARTIAL_PUB_FIELDS, field.vis.span, msg, None, From abd5db332173a5ef76a56f4fd18af421cf551c17 Mon Sep 17 00:00:00 2001 From: TennyZhuang Date: Sun, 16 Oct 2022 16:57:31 +0800 Subject: [PATCH 040/524] add many tests Signed-off-by: TennyZhuang --- tests/ui/partial_pub_fields.rs | 24 ++++++++++++++++++++++-- tests/ui/partial_pub_fields.stderr | 22 +++++++++++++++++++--- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/tests/ui/partial_pub_fields.rs b/tests/ui/partial_pub_fields.rs index e1a4b4827991..668545da8441 100644 --- a/tests/ui/partial_pub_fields.rs +++ b/tests/ui/partial_pub_fields.rs @@ -2,8 +2,6 @@ #![warn(clippy::partial_pub_fields)] fn main() { - // test code goes here - use std::collections::HashMap; #[derive(Default)] @@ -17,4 +15,26 @@ fn main() { pub g: u8, b: u8, } + + pub struct Point(i32, pub i32); + + pub struct Visibility { + r#pub: bool, + pub pos: u32, + } + + // Don't lint on empty structs; + pub struct Empty1; + pub struct Empty2(); + pub struct Empty3 {}; + + // Don't lint on structs with one field. + pub struct Single1(i32); + pub struct Single2(pub i32); + pub struct Single3 { + v1: i32, + } + pub struct Single4 { + pub v1: i32, + } } diff --git a/tests/ui/partial_pub_fields.stderr b/tests/ui/partial_pub_fields.stderr index f7f23c85a67e..84cfc1a91940 100644 --- a/tests/ui/partial_pub_fields.stderr +++ b/tests/ui/partial_pub_fields.stderr @@ -1,5 +1,5 @@ error: mixed usage of pub and non-pub fields - --> $DIR/partial_pub_fields.rs:12:9 + --> $DIR/partial_pub_fields.rs:10:9 | LL | pub paths: HashMap, | ^^^ @@ -8,12 +8,28 @@ LL | pub paths: HashMap, = note: `-D clippy::partial-pub-fields` implied by `-D warnings` error: mixed usage of pub and non-pub fields - --> $DIR/partial_pub_fields.rs:18:9 + --> $DIR/partial_pub_fields.rs:16:9 | LL | b: u8, | ^ | = help: consider using public field here -error: aborting due to 2 previous errors +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:19:27 + | +LL | pub struct Point(i32, pub i32); + | ^^^ + | + = help: consider using private field here + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:23:9 + | +LL | pub pos: u32, + | ^^^ + | + = help: consider using private field here + +error: aborting due to 4 previous errors From 360b48b1ab97471c0d122732a027b65f46980447 Mon Sep 17 00:00:00 2001 From: TennyZhuang Date: Sun, 16 Oct 2022 17:10:27 +0800 Subject: [PATCH 041/524] fix a doctest Signed-off-by: TennyZhuang --- clippy_lints/src/partial_pub_fields.rs | 12 ++++++------ src/docs/partial_pub_fields.txt | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs index 42f892e3652a..f60d9d65b120 100644 --- a/clippy_lints/src/partial_pub_fields.rs +++ b/clippy_lints/src/partial_pub_fields.rs @@ -18,17 +18,17 @@ declare_clippy_lint! { /// ### Example /// ```rust /// pub struct Color { - /// pub r, - /// pub g, - /// b, + /// pub r: u8, + /// pub g: u8, + /// b: u8, /// } /// ``` /// Use instead: /// ```rust /// pub struct Color { - /// pub r, - /// pub g, - /// pub b, + /// pub r: u8, + /// pub g: u8, + /// pub b: u8, /// } /// ``` #[clippy::version = "1.66.0"] diff --git a/src/docs/partial_pub_fields.txt b/src/docs/partial_pub_fields.txt index a332ec8c28a6..b529adf1547d 100644 --- a/src/docs/partial_pub_fields.txt +++ b/src/docs/partial_pub_fields.txt @@ -12,16 +12,16 @@ interior invariants and expose intentionally limited API to the outside world. ### Example ``` pub struct Color { - pub r, - pub g, - b, + pub r: u8, + pub g: u8, + b: u8, } ``` Use instead: ``` pub struct Color { - pub r, - pub g, - pub b, + pub r: u8, + pub g: u8, + pub b: u8, } ``` \ No newline at end of file From 136c2cdb910938103a762cdde177b1633bdbf99a Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 13 Oct 2022 12:13:54 +0000 Subject: [PATCH 042/524] Add `unused_format_specs` lint --- CHANGELOG.md | 1 + clippy_lints/src/format_args.rs | 151 +++++++++++++++--- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_complexity.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_utils/src/macros.rs | 43 ++++- src/docs.rs | 1 + src/docs/unused_format_specs.txt | 24 +++ tests/ui/unused_format_specs.fixed | 18 +++ tests/ui/unused_format_specs.rs | 18 +++ tests/ui/unused_format_specs.stderr | 54 +++++++ tests/ui/unused_format_specs_unfixable.rs | 30 ++++ tests/ui/unused_format_specs_unfixable.stderr | 69 ++++++++ 13 files changed, 384 insertions(+), 28 deletions(-) create mode 100644 src/docs/unused_format_specs.txt create mode 100644 tests/ui/unused_format_specs.fixed create mode 100644 tests/ui/unused_format_specs.rs create mode 100644 tests/ui/unused_format_specs.stderr create mode 100644 tests/ui/unused_format_specs_unfixable.rs create mode 100644 tests/ui/unused_format_specs_unfixable.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index f593966c0459..5b2f96ed1ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4315,6 +4315,7 @@ Released 2018-09-13 [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice [`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect +[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 4f06fc8fa5a2..4c4a1e06cd43 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; -use clippy_utils::macros::{is_format_macro, is_panic, FormatArgsExpn, FormatParam, FormatParamUsage}; +use clippy_utils::macros::{ + is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage, +}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; use if_chain::if_chain; use itertools::Itertools; @@ -117,7 +119,43 @@ declare_clippy_lint! { "using non-inlined variables in `format!` calls" } -impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); +declare_clippy_lint! { + /// ### What it does + /// Detects [formatting parameters] that have no effect on the output of + /// `format!()`, `println!()` or similar macros. + /// + /// ### Why is this bad? + /// Shorter format specifiers are easier to read, it may also indicate that + /// an expected formatting operation such as adding padding isn't happening. + /// + /// ### Example + /// ```rust + /// println!("{:.}", 1.0); + /// + /// println!("not padded: {:5}", format_args!("...")); + /// ``` + /// Use instead: + /// ```rust + /// println!("{}", 1.0); + /// + /// println!("not padded: {}", format_args!("...")); + /// // OR + /// println!("padded: {:5}", format!("...")); + /// ``` + /// + /// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters + #[clippy::version = "1.66.0"] + pub UNUSED_FORMAT_SPECS, + complexity, + "use of a format specifier that has no effect" +} + +impl_lint_pass!(FormatArgs => [ + FORMAT_IN_FORMAT_ARGS, + TO_STRING_IN_FORMAT_ARGS, + UNINLINED_FORMAT_ARGS, + UNUSED_FORMAT_SPECS, +]); pub struct FormatArgs { msrv: Option, @@ -132,27 +170,26 @@ impl FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let Some(format_args) = FormatArgsExpn::parse(cx, expr); - let expr_expn_data = expr.span.ctxt().outer_expn_data(); - let outermost_expn_data = outermost_expn_data(expr_expn_data); - if let Some(macro_def_id) = outermost_expn_data.macro_def_id; - if is_format_macro(cx, macro_def_id); - if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; - then { - for arg in &format_args.args { - if !arg.format.is_default() { - continue; - } - if is_aliased(&format_args, arg.param.value.hir_id) { - continue; - } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); - check_to_string_in_format_args(cx, name, arg.param.value); + if let Some(format_args) = FormatArgsExpn::parse(cx, expr) + && let expr_expn_data = expr.span.ctxt().outer_expn_data() + && let outermost_expn_data = outermost_expn_data(expr_expn_data) + && let Some(macro_def_id) = outermost_expn_data.macro_def_id + && is_format_macro(cx, macro_def_id) + && let ExpnKind::Macro(_, name) = outermost_expn_data.kind + { + for arg in &format_args.args { + check_unused_format_specifier(cx, arg); + if !arg.format.is_default() { + continue; } - if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id); + if is_aliased(&format_args, arg.param.value.hir_id) { + continue; } + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); + check_to_string_in_format_args(cx, name, arg.param.value); + } + if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id); } } } @@ -160,6 +197,76 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { extract_msrv_attr!(LateContext); } +fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { + let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs(); + + if let Count::Implied(Some(mut span)) = arg.format.precision + && !span.is_empty() + { + span_lint_and_then( + cx, + UNUSED_FORMAT_SPECS, + span, + "empty precision specifier has no effect", + |diag| { + if param_ty.is_floating_point() { + diag.note("a precision specifier is not required to format floats"); + } + + if arg.format.is_default() { + // If there's no other specifiers remove the `:` too + span = arg.format_span(); + } + + diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable); + }, + ); + } + + if is_type_diagnostic_item(cx, param_ty, sym::Arguments) && !arg.format.is_default_for_trait() { + span_lint_and_then( + cx, + UNUSED_FORMAT_SPECS, + arg.span, + "format specifiers have no effect on `format_args!()`", + |diag| { + let mut suggest_format = |spec, span| { + let message = format!("for the {spec} to apply consider using `format!()`"); + + if let Some(mac_call) = root_macro_call(arg.param.value.span) + && cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id) + && arg.span.eq_ctxt(mac_call.span) + { + diag.span_suggestion( + cx.sess().source_map().span_until_char(mac_call.span, '!'), + message, + "format", + Applicability::MaybeIncorrect, + ); + } else if let Some(span) = span { + diag.span_help(span, message); + } + }; + + if !arg.format.width.is_implied() { + suggest_format("width", arg.format.width.span()); + } + + if !arg.format.precision.is_implied() { + suggest_format("precision", arg.format.precision.span()); + } + + diag.span_suggestion_verbose( + arg.format_span(), + "if the current behavior is intentional, remove the format specifiers", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + } +} + fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) { if args.format_string.span.from_expansion() { return; diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 04f0da4b2fe7..987131ab7271 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -73,6 +73,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), LintId::of(format_args::UNINLINED_FORMAT_ARGS), + LintId::of(format_args::UNUSED_FORMAT_SPECS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), LintId::of(formatting::POSSIBLE_MISSING_COMMA), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index e3849e5a626b..8be9dc4baf19 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -13,6 +13,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(double_parens::DOUBLE_PARENS), LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), + LintId::of(format_args::UNUSED_FORMAT_SPECS), LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(int_plus_one::INT_PLUS_ONE), LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index de1253c8510a..049ebdc926ec 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -164,6 +164,7 @@ store.register_lints(&[ format_args::FORMAT_IN_FORMAT_ARGS, format_args::TO_STRING_IN_FORMAT_ARGS, format_args::UNINLINED_FORMAT_ARGS, + format_args::UNUSED_FORMAT_SPECS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, format_push_string::FORMAT_PUSH_STRING, diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 5a63c290a315..9a682fbe604f 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -627,7 +627,7 @@ pub enum Count<'tcx> { /// `FormatParamKind::Numbered`. Param(FormatParam<'tcx>), /// Not specified. - Implied, + Implied(Option), } impl<'tcx> Count<'tcx> { @@ -638,8 +638,10 @@ impl<'tcx> Count<'tcx> { inner: Option, values: &FormatArgsValues<'tcx>, ) -> Option { + let span = inner.map(|inner| span_from_inner(values.format_string_span, inner)); + Some(match count { - rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)), + rpf::Count::CountIs(val) => Self::Is(val, span?), rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new( FormatParamKind::Named(Symbol::intern(name)), usage, @@ -661,12 +663,12 @@ impl<'tcx> Count<'tcx> { inner?, values, )?), - rpf::Count::CountImplied => Self::Implied, + rpf::Count::CountImplied => Self::Implied(span), }) } pub fn is_implied(self) -> bool { - matches!(self, Count::Implied) + matches!(self, Count::Implied(_)) } pub fn param(self) -> Option> { @@ -675,6 +677,14 @@ impl<'tcx> Count<'tcx> { _ => None, } } + + pub fn span(self) -> Option { + match self { + Count::Is(_, span) => Some(span), + Count::Param(param) => Some(param.span), + Count::Implied(span) => span, + } + } } /// Specification for the formatting of an argument in the format string. See @@ -738,8 +748,13 @@ impl<'tcx> FormatSpec<'tcx> { /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`, /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}` pub fn is_default(&self) -> bool { - self.r#trait == sym::Display - && self.width.is_implied() + self.r#trait == sym::Display && self.is_default_for_trait() + } + + /// Has no other formatting specifiers than setting the format trait. returns true for `{}`, + /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}` + pub fn is_default_for_trait(&self) -> bool { + self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.flags == 0 @@ -757,6 +772,22 @@ pub struct FormatArg<'tcx> { pub span: Span, } +impl<'tcx> FormatArg<'tcx> { + /// Span of the `:` and format specifiers + /// + /// ```ignore + /// format!("{:.}"), format!("{foo:.}") + /// ^^ ^^ + /// ``` + pub fn format_span(&self) -> Span { + let base = self.span.data(); + + // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing + // brace `{...|}` + Span::new(self.param.span.hi(), base.hi - BytePos(1), base.ctxt, base.parent) + } +} + /// A parsed `format_args!` expansion. #[derive(Debug)] pub struct FormatArgsExpn<'tcx> { diff --git a/src/docs.rs b/src/docs.rs index 41c31f91bca5..33ea84883750 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -557,6 +557,7 @@ docs! { "unseparated_literal_suffix", "unsound_collection_transmute", "unused_async", + "unused_format_specs", "unused_io_amount", "unused_peekable", "unused_rounding", diff --git a/src/docs/unused_format_specs.txt b/src/docs/unused_format_specs.txt new file mode 100644 index 000000000000..77be3a2fb170 --- /dev/null +++ b/src/docs/unused_format_specs.txt @@ -0,0 +1,24 @@ +### What it does +Detects [formatting parameters] that have no effect on the output of +`format!()`, `println!()` or similar macros. + +### Why is this bad? +Shorter format specifiers are easier to read, it may also indicate that +an expected formatting operation such as adding padding isn't happening. + +### Example +``` +println!("{:.}", 1.0); + +println!("not padded: {:5}", format_args!("...")); +``` +Use instead: +``` +println!("{}", 1.0); + +println!("not padded: {}", format_args!("...")); +// OR +println!("padded: {:5}", format!("...")); +``` + +[formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters \ No newline at end of file diff --git a/tests/ui/unused_format_specs.fixed b/tests/ui/unused_format_specs.fixed new file mode 100644 index 000000000000..2930722b42d9 --- /dev/null +++ b/tests/ui/unused_format_specs.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +fn main() { + let f = 1.0f64; + println!("{}", 1.0); + println!("{f} {f:?}"); + + println!("{}", 1); +} + +fn should_not_lint() { + let f = 1.0f64; + println!("{:.1}", 1.0); + println!("{f:.w$} {f:.*?}", 3, w = 2); +} diff --git a/tests/ui/unused_format_specs.rs b/tests/ui/unused_format_specs.rs new file mode 100644 index 000000000000..ee192a000d4b --- /dev/null +++ b/tests/ui/unused_format_specs.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +fn main() { + let f = 1.0f64; + println!("{:.}", 1.0); + println!("{f:.} {f:.?}"); + + println!("{:.}", 1); +} + +fn should_not_lint() { + let f = 1.0f64; + println!("{:.1}", 1.0); + println!("{f:.w$} {f:.*?}", 3, w = 2); +} diff --git a/tests/ui/unused_format_specs.stderr b/tests/ui/unused_format_specs.stderr new file mode 100644 index 000000000000..7231c17e74c1 --- /dev/null +++ b/tests/ui/unused_format_specs.stderr @@ -0,0 +1,54 @@ +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:8:17 + | +LL | println!("{:.}", 1.0); + | ^ + | + = note: a precision specifier is not required to format floats + = note: `-D clippy::unused-format-specs` implied by `-D warnings` +help: remove the `.` + | +LL - println!("{:.}", 1.0); +LL + println!("{}", 1.0); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:9:18 + | +LL | println!("{f:.} {f:.?}"); + | ^ + | + = note: a precision specifier is not required to format floats +help: remove the `.` + | +LL - println!("{f:.} {f:.?}"); +LL + println!("{f} {f:.?}"); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:9:24 + | +LL | println!("{f:.} {f:.?}"); + | ^ + | + = note: a precision specifier is not required to format floats +help: remove the `.` + | +LL - println!("{f:.} {f:.?}"); +LL + println!("{f:.} {f:?}"); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:11:17 + | +LL | println!("{:.}", 1); + | ^ + | +help: remove the `.` + | +LL - println!("{:.}", 1); +LL + println!("{}", 1); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unused_format_specs_unfixable.rs b/tests/ui/unused_format_specs_unfixable.rs new file mode 100644 index 000000000000..78601a3483d3 --- /dev/null +++ b/tests/ui/unused_format_specs_unfixable.rs @@ -0,0 +1,30 @@ +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +macro_rules! format_args_from_macro { + () => { + format_args!("from macro") + }; +} + +fn main() { + // prints `.`, not ` .` + println!("{:5}.", format_args!("")); + //prints `abcde`, not `abc` + println!("{:.3}", format_args!("abcde")); + + println!("{:5}.", format_args_from_macro!()); + + let args = format_args!(""); + println!("{args:5}"); +} + +fn should_not_lint() { + println!("{}", format_args!("")); + // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use + // debug formatting so allow it + println!("{:?}", format_args!("")); + + let args = format_args!(""); + println!("{args}"); +} diff --git a/tests/ui/unused_format_specs_unfixable.stderr b/tests/ui/unused_format_specs_unfixable.stderr new file mode 100644 index 000000000000..9f1890282e6a --- /dev/null +++ b/tests/ui/unused_format_specs_unfixable.stderr @@ -0,0 +1,69 @@ +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:12:15 + | +LL | println!("{:5}.", format_args!("")); + | ^^^^ + | + = note: `-D clippy::unused-format-specs` implied by `-D warnings` +help: for the width to apply consider using `format!()` + | +LL | println!("{:5}.", format!("")); + | ~~~~~~ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:5}.", format_args!("")); +LL + println!("{}.", format_args!("")); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:14:15 + | +LL | println!("{:.3}", format_args!("abcde")); + | ^^^^^ + | +help: for the precision to apply consider using `format!()` + | +LL | println!("{:.3}", format!("abcde")); + | ~~~~~~ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:.3}", format_args!("abcde")); +LL + println!("{}", format_args!("abcde")); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:16:15 + | +LL | println!("{:5}.", format_args_from_macro!()); + | ^^^^ + | +help: for the width to apply consider using `format!()` + --> $DIR/unused_format_specs_unfixable.rs:16:17 + | +LL | println!("{:5}.", format_args_from_macro!()); + | ^ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:5}.", format_args_from_macro!()); +LL + println!("{}.", format_args_from_macro!()); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:19:15 + | +LL | println!("{args:5}"); + | ^^^^^^^^ + | +help: for the width to apply consider using `format!()` + --> $DIR/unused_format_specs_unfixable.rs:19:21 + | +LL | println!("{args:5}"); + | ^ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{args:5}"); +LL + println!("{args}"); + | + +error: aborting due to 4 previous errors + From 831b99436c3dc47eafdaf363acd1d4e9e230551d Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Wed, 19 Oct 2022 00:08:20 +0200 Subject: [PATCH 043/524] Implement -Ztrack-diagnostics --- clippy_lints/src/doc.rs | 1 + src/driver.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 36dc7e3396b8..9e2facf0f63b 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -691,6 +691,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { false, None, false, + false, ); let handler = Handler::with_emitter(false, None, Box::new(emitter)); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/driver.rs b/src/driver.rs index b12208ac62a8..ae54b2078a65 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -179,6 +179,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { false, None, false, + false, )); let handler = rustc_errors::Handler::with_emitter(true, None, emitter); From 1da1ff6b3c01bef0fbdcfae278480717bb54582c Mon Sep 17 00:00:00 2001 From: royrustdev Date: Wed, 12 Oct 2022 18:01:08 +0530 Subject: [PATCH 044/524] Update Applicability of `redundant_allocation` lint from `MachineApplicable` to `Unspecified` --- clippy_lints/src/types/redundant_allocation.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 92d2c48a5898..7883353e3fef 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -10,6 +10,7 @@ use rustc_span::symbol::sym; use super::{utils, REDUNDANT_ALLOCATION}; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + let mut applicability = Applicability::MaybeIncorrect; let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { "Box" } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { @@ -21,7 +22,6 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; if let Some(span) = utils::match_borrows_parameter(cx, qpath) { - let mut applicability = Applicability::MaybeIncorrect; let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_then( cx, @@ -63,7 +63,6 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ None => return false, }; if inner_sym == outer_sym { - let mut applicability = Applicability::MaybeIncorrect; let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability); span_lint_and_then( cx, From b78509e2f29c12594cb8c9633ef89383f1536e5a Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 19 Oct 2022 11:46:26 +0800 Subject: [PATCH 045/524] Add testcase for next_point, fix more trivial issues in find_width_of_character_at_span --- clippy_utils/src/sugg.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3c5dd92b9cd6..3347342e412b 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -769,8 +769,7 @@ impl DiagnosticExt for rustc_errors::Diagnostic { fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) { let mut remove_span = item; - let hi = cx.sess().source_map().next_point(remove_span).hi(); - let fmpos = cx.sess().source_map().lookup_byte_offset(hi); + let fmpos = cx.sess().source_map().lookup_byte_offset(remove_span.hi()); if let Some(ref src) = fmpos.sf.src { let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n'); From 4dfc7b20250b040b34953ccf71b1bbef77a75f7d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 19 Oct 2022 11:34:00 -0700 Subject: [PATCH 046/524] Fixup a few tests needing asm support --- tests/ui/entry.fixed | 1 + tests/ui/entry.rs | 1 + tests/ui/entry.stderr | 20 ++++++++-------- tests/ui/missing_doc.rs | 1 + tests/ui/missing_doc.stderr | 48 ++++++++++++++++++------------------- 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index e43635abcd11..79c29c04e059 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -1,3 +1,4 @@ +// needs-asm-support // run-rustfix #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index d999b3b7dc80..2d7985457d8b 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -1,3 +1,4 @@ +// needs-asm-support // run-rustfix #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index 2ef9966525ce..2c4c49d2522c 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -1,5 +1,5 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:24:5 + --> $DIR/entry.rs:25:5 | LL | / if !m.contains_key(&k) { LL | | m.insert(k, v); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::map-entry` implied by `-D warnings` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:29:5 + --> $DIR/entry.rs:30:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -32,7 +32,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:38:5 + --> $DIR/entry.rs:39:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -55,7 +55,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:47:5 + --> $DIR/entry.rs:48:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -79,7 +79,7 @@ LL + } | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:57:5 + --> $DIR/entry.rs:58:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -96,7 +96,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:63:5 + --> $DIR/entry.rs:64:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -122,7 +122,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:75:5 + --> $DIR/entry.rs:76:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -146,7 +146,7 @@ LL + } | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:85:5 + --> $DIR/entry.rs:86:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -187,7 +187,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:119:5 + --> $DIR/entry.rs:120:5 | LL | / if !m.contains_key(&m!(k)) { LL | | m.insert(m!(k), m!(v)); @@ -195,7 +195,7 @@ LL | | } | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:151:5 + --> $DIR/entry.rs:152:5 | LL | / if !m.contains_key(&k) { LL | | let x = (String::new(), String::new()); diff --git a/tests/ui/missing_doc.rs b/tests/ui/missing_doc.rs index 29cc026a8fd3..590ad63c90be 100644 --- a/tests/ui/missing_doc.rs +++ b/tests/ui/missing_doc.rs @@ -1,3 +1,4 @@ +// needs-asm-support // aux-build: proc_macro_with_span.rs #![warn(clippy::missing_docs_in_private_items)] diff --git a/tests/ui/missing_doc.stderr b/tests/ui/missing_doc.stderr index 6c8e66f46437..d3bef28bf64c 100644 --- a/tests/ui/missing_doc.stderr +++ b/tests/ui/missing_doc.stderr @@ -1,5 +1,5 @@ error: missing documentation for a type alias - --> $DIR/missing_doc.rs:15:1 + --> $DIR/missing_doc.rs:16:1 | LL | type Typedef = String; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -7,37 +7,37 @@ LL | type Typedef = String; = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` error: missing documentation for a type alias - --> $DIR/missing_doc.rs:16:1 + --> $DIR/missing_doc.rs:17:1 | LL | pub type PubTypedef = String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:18:1 + --> $DIR/missing_doc.rs:19:1 | LL | mod module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:19:1 + --> $DIR/missing_doc.rs:20:1 | LL | pub mod pub_module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:23:1 + --> $DIR/missing_doc.rs:24:1 | LL | pub fn foo2() {} | ^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:24:1 + --> $DIR/missing_doc.rs:25:1 | LL | fn foo3() {} | ^^^^^^^^^^^^ error: missing documentation for an enum - --> $DIR/missing_doc.rs:38:1 + --> $DIR/missing_doc.rs:39:1 | LL | / enum Baz { LL | | BazA { a: isize, b: isize }, @@ -46,31 +46,31 @@ LL | | } | |_^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:39:5 + --> $DIR/missing_doc.rs:40:5 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:39:12 + --> $DIR/missing_doc.rs:40:12 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:39:22 + --> $DIR/missing_doc.rs:40:22 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:40:5 + --> $DIR/missing_doc.rs:41:5 | LL | BarB, | ^^^^ error: missing documentation for an enum - --> $DIR/missing_doc.rs:43:1 + --> $DIR/missing_doc.rs:44:1 | LL | / pub enum PubBaz { LL | | PubBazA { a: isize }, @@ -78,43 +78,43 @@ LL | | } | |_^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:44:5 + --> $DIR/missing_doc.rs:45:5 | LL | PubBazA { a: isize }, | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:44:15 + --> $DIR/missing_doc.rs:45:15 | LL | PubBazA { a: isize }, | ^^^^^^^^ error: missing documentation for a constant - --> $DIR/missing_doc.rs:64:1 + --> $DIR/missing_doc.rs:65:1 | LL | const FOO: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ error: missing documentation for a constant - --> $DIR/missing_doc.rs:71:1 + --> $DIR/missing_doc.rs:72:1 | LL | pub const FOO4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/missing_doc.rs:73:1 + --> $DIR/missing_doc.rs:74:1 | LL | static BAR: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/missing_doc.rs:80:1 + --> $DIR/missing_doc.rs:81:1 | LL | pub static BAR4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:82:1 + --> $DIR/missing_doc.rs:83:1 | LL | / mod internal_impl { LL | | /// dox @@ -126,31 +126,31 @@ LL | | } | |_^ error: missing documentation for a function - --> $DIR/missing_doc.rs:85:5 + --> $DIR/missing_doc.rs:86:5 | LL | pub fn undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:86:5 + --> $DIR/missing_doc.rs:87:5 | LL | pub fn undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:87:5 + --> $DIR/missing_doc.rs:88:5 | LL | fn undocumented3() {} | ^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:92:9 + --> $DIR/missing_doc.rs:93:9 | LL | pub fn also_undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:93:9 + --> $DIR/missing_doc.rs:94:9 | LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ From 56506730c827e02a69cb803fcaa7e912cf25826b Mon Sep 17 00:00:00 2001 From: Kevin Per Date: Tue, 11 Oct 2022 16:17:59 +0000 Subject: [PATCH 047/524] Implement assertions and fixes to not emit empty spans without suggestions --- clippy_lints/src/manual_assert.rs | 12 ++--- clippy_lints/src/needless_late_init.rs | 11 +++-- tests/ui/manual_assert.edition2018.stderr | 55 ++++------------------- tests/ui/manual_assert.edition2021.stderr | 55 ++++------------------- 4 files changed, 30 insertions(+), 103 deletions(-) diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 825ec84b4a81..b8ed9b9ec18f 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -69,11 +69,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { "only a `panic!` in `if`-then statement", |diag| { // comments can be noisy, do not show them to the user - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability); + if !comments.is_empty() { + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability); + } diag.span_suggestion( expr.span, "try instead", diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 9d26e5900866..67debe7e08af 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -180,10 +180,13 @@ fn assignment_suggestions<'tcx>( let suggestions = assignments .iter() .flat_map(|assignment| { - [ - assignment.span.until(assignment.rhs_span), - assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()), - ] + let mut spans = vec![assignment.span.until(assignment.rhs_span)]; + + if assignment.rhs_span.hi() != assignment.span.hi() { + spans.push(assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi())); + } + + spans }) .map(|span| (span, String::new())) .collect::>(); diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 7718588fdf6f..237638ee1344 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -4,13 +4,9 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ + | |_____^ help: try instead: `assert!(a.is_empty(), "qaqaq{:?}", a);` | = note: `-D clippy::manual-assert` implied by `-D warnings` -help: try instead - | -LL | assert!(a.is_empty(), "qaqaq{:?}", a); - | error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:34:5 @@ -18,12 +14,7 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(a.is_empty(), "qwqwq"); - | + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:51:5 @@ -31,12 +22,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!b.is_empty(), "panic1"); - | + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:54:5 @@ -44,12 +30,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:57:5 @@ -57,12 +38,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:60:5 @@ -70,12 +46,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:63:5 @@ -83,12 +54,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 @@ -96,12 +62,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!a.is_empty(), "with expansion {}", one!()); - | + | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:73:5 diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 7718588fdf6f..237638ee1344 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -4,13 +4,9 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ + | |_____^ help: try instead: `assert!(a.is_empty(), "qaqaq{:?}", a);` | = note: `-D clippy::manual-assert` implied by `-D warnings` -help: try instead - | -LL | assert!(a.is_empty(), "qaqaq{:?}", a); - | error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:34:5 @@ -18,12 +14,7 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(a.is_empty(), "qwqwq"); - | + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:51:5 @@ -31,12 +22,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!b.is_empty(), "panic1"); - | + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:54:5 @@ -44,12 +30,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:57:5 @@ -57,12 +38,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:60:5 @@ -70,12 +46,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:63:5 @@ -83,12 +54,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 @@ -96,12 +62,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!a.is_empty(), "with expansion {}", one!()); - | + | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:73:5 From b6a860e0ed7a76b1834c9618842d299e4501d2ff Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 17 Oct 2022 21:59:27 +0000 Subject: [PATCH 048/524] Add `missing_trait_methods` lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_restriction.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/missing_trait_methods.rs | 98 ++++++++++++++++++++ src/docs.rs | 1 + src/docs/missing_trait_methods.txt | 40 ++++++++ tests/ui/missing_trait_methods.rs | 50 ++++++++++ tests/ui/missing_trait_methods.stderr | 27 ++++++ 9 files changed, 221 insertions(+) create mode 100644 clippy_lints/src/missing_trait_methods.rs create mode 100644 src/docs/missing_trait_methods.txt create mode 100644 tests/ui/missing_trait_methods.rs create mode 100644 tests/ui/missing_trait_methods.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e0ff5db0ee7..a9476e8373e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4049,6 +4049,7 @@ Released 2018-09-13 [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop +[`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals [`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index c188e9057f12..03bfc7de56e4 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -407,6 +407,7 @@ store.register_lints(&[ missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + missing_trait_methods::MISSING_TRAIT_METHODS, mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION, mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, module_style::MOD_MODULE_FILES, diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 9edced28408f..f62d57af5b47 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -47,6 +47,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), + LintId::of(missing_trait_methods::MISSING_TRAIT_METHODS), LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION), LintId::of(module_style::MOD_MODULE_FILES), LintId::of(module_style::SELF_NAMED_MODULE_FILES), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b9185b8a5149..9418e0e1135a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -289,6 +289,7 @@ mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; mod missing_inline; +mod missing_trait_methods; mod mixed_read_write_in_expression; mod module_style; mod multi_assignments; @@ -916,6 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(box_default::BoxDefault)); store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields)); + store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs new file mode 100644 index 000000000000..68af8a672f6a --- /dev/null +++ b/clippy_lints/src/missing_trait_methods.rs @@ -0,0 +1,98 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_lint_allowed; +use clippy_utils::macros::span_is_local; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::AssocItem; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks if a provided method is used implicitly by a trait + /// implementation. A usage example would be a wrapper where every method + /// should perform some operation before delegating to the inner type's + /// implemenation. + /// + /// This lint should typically be enabled on a specific trait `impl` item + /// rather than globally. + /// + /// ### Why is this bad? + /// Indicates that a method is missing. + /// + /// ### Example + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// } + /// ``` + /// Use instead: + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// + /// fn provided() { /* ... */ } + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub MISSING_TRAIT_METHODS, + restriction, + "trait implementation uses default provided method" +} +declare_lint_pass!(MissingTraitMethods => [MISSING_TRAIT_METHODS]); + +impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id()) + && span_is_local(item.span) + && let ItemKind::Impl(Impl { + items, + of_trait: Some(trait_ref), + .. + }) = item.kind + && let Some(trait_id) = trait_ref.trait_def_id() + { + let mut provided: DefIdMap<&AssocItem> = cx + .tcx + .provided_trait_methods(trait_id) + .map(|assoc| (assoc.def_id, assoc)) + .collect(); + + for impl_item in *items { + if let Some(def_id) = impl_item.trait_item_def_id { + provided.remove(&def_id); + } + } + + for assoc in provided.values() { + let source_map = cx.tcx.sess.source_map(); + let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); + + span_lint_and_help( + cx, + MISSING_TRAIT_METHODS, + source_map.guess_head_span(item.span), + &format!("missing trait method provided by default: `{}`", assoc.name), + Some(definition_span), + "implement the method", + ); + } + } + } +} diff --git a/src/docs.rs b/src/docs.rs index b8b4286b488a..8aaa61d2967d 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -317,6 +317,7 @@ docs! { "missing_panics_doc", "missing_safety_doc", "missing_spin_loop", + "missing_trait_methods", "mistyped_literal_suffixes", "mixed_case_hex_literals", "mixed_read_write_in_expression", diff --git a/src/docs/missing_trait_methods.txt b/src/docs/missing_trait_methods.txt new file mode 100644 index 000000000000..788ad764f8c3 --- /dev/null +++ b/src/docs/missing_trait_methods.txt @@ -0,0 +1,40 @@ +### What it does +Checks if a provided method is used implicitly by a trait +implementation. A usage example would be a wrapper where every method +should perform some operation before delegating to the inner type's +implemenation. + +This lint should typically be enabled on a specific trait `impl` item +rather than globally. + +### Why is this bad? +Indicates that a method is missing. + +### Example +``` +trait Trait { + fn required(); + + fn provided() {} +} + +#[warn(clippy::missing_trait_methods)] +impl Trait for Type { + fn required() { /* ... */ } +} +``` +Use instead: +``` +trait Trait { + fn required(); + + fn provided() {} +} + +#[warn(clippy::missing_trait_methods)] +impl Trait for Type { + fn required() { /* ... */ } + + fn provided() { /* ... */ } +} +``` \ No newline at end of file diff --git a/tests/ui/missing_trait_methods.rs b/tests/ui/missing_trait_methods.rs new file mode 100644 index 000000000000..8df885919a3e --- /dev/null +++ b/tests/ui/missing_trait_methods.rs @@ -0,0 +1,50 @@ +#![allow(unused, clippy::needless_lifetimes)] +#![warn(clippy::missing_trait_methods)] + +trait A { + fn provided() {} +} + +trait B { + fn required(); + + fn a(_: usize) -> usize { + 1 + } + + fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { + a.as_ref() + } +} + +struct Partial; + +impl A for Partial {} + +impl B for Partial { + fn required() {} + + fn a(_: usize) -> usize { + 2 + } +} + +struct Complete; + +impl A for Complete { + fn provided() {} +} + +impl B for Complete { + fn required() {} + + fn a(_: usize) -> usize { + 2 + } + + fn b>(a: &T) -> &[u8] { + a.as_ref() + } +} + +fn main() {} diff --git a/tests/ui/missing_trait_methods.stderr b/tests/ui/missing_trait_methods.stderr new file mode 100644 index 000000000000..0c5205e19657 --- /dev/null +++ b/tests/ui/missing_trait_methods.stderr @@ -0,0 +1,27 @@ +error: missing trait method provided by default: `provided` + --> $DIR/missing_trait_methods.rs:22:1 + | +LL | impl A for Partial {} + | ^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> $DIR/missing_trait_methods.rs:5:5 + | +LL | fn provided() {} + | ^^^^^^^^^^^^^ + = note: `-D clippy::missing-trait-methods` implied by `-D warnings` + +error: missing trait method provided by default: `b` + --> $DIR/missing_trait_methods.rs:24:1 + | +LL | impl B for Partial { + | ^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> $DIR/missing_trait_methods.rs:15:5 + | +LL | fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 4ff2364ff76769e9942956d4f8a81fca77c72e22 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 20 Oct 2022 16:39:44 +0200 Subject: [PATCH 049/524] Bump nightly version -> 2022-10-20 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 49b13cb54e71..748d8a317160 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-10-06" +channel = "nightly-2022-10-20" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 059e52b96b0127a4b6e7f566a2023482e34c27f6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 20 Oct 2022 17:51:48 +0200 Subject: [PATCH 050/524] rustc_hir_typeck: fix clippy --- clippy_lints/src/escape.rs | 4 ++-- clippy_lints/src/lib.rs | 1 + clippy_lints/src/loops/mut_range_bound.rs | 4 ++-- clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- clippy_lints/src/operators/assign_op_pattern.rs | 2 +- clippy_lints/src/transmute/utils.rs | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/sugg.rs | 7 +++---- clippy_utils/src/usage.rs | 7 +++---- 10 files changed, 17 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index eb0455ae404c..c9a8307eba4f 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -178,7 +178,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, ) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2dcefd78763b..89ffca8128a9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -32,6 +32,7 @@ extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_analysis; +extern crate rustc_hir_typeck; extern crate rustc_hir_pretty; extern crate rustc_index; extern crate rustc_infer; diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index db73ab55b37c..91b321c44747 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -4,7 +4,7 @@ use clippy_utils::{get_enclosing_block, higher, path_to_local}; use if_chain::if_chain; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::{mir::FakeReadCause, ty}; @@ -115,7 +115,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, ) { diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 6017941452c0..5cf88bfc8880 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -8,7 +8,7 @@ use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trai use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; -use rustc_hir_analysis::check::{FnCtxt, Inherited}; +use rustc_hir_typeck::{FnCtxt, Inherited}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7f881e27dd27..9c949a28f44d 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -12,7 +12,7 @@ use rustc_hir::{ BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind, }; use rustc_hir::{HirIdMap, HirIdSet}; -use rustc_hir_analysis::expr_use_visitor as euv; +use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -342,7 +342,7 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, ) { diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index c7e964cf23e2..ee9fd94064c0 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -8,7 +8,7 @@ use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::BorrowKind; diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 102f7541c8ce..70d166c4854c 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,5 +1,5 @@ use rustc_hir::Expr; -use rustc_hir_analysis::check::{cast, FnCtxt, Inherited}; +use rustc_hir_typeck::{cast, FnCtxt, Inherited}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e7e3625c078a..7e42fcc6569b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -24,7 +24,7 @@ extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; -extern crate rustc_hir_analysis; +extern crate rustc_hir_typeck; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3347342e412b..5089987ef720 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -10,7 +10,7 @@ use rustc_ast_pretty::pprust::token_kind_to_string; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; @@ -1054,11 +1054,10 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, - ) { - } + ) {} } #[cfg(test)] diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index e32bae6ed1fd..000fb51c0185 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -5,7 +5,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; use rustc_hir::{Expr, ExprKind, HirId, Node}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -75,11 +75,10 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { fn fake_read( &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, + _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId, - ) { - } + ) {} } pub struct ParamBindingIdCollector { From 615b7617ed3ba1ea44575a9072b17a4bdfb565b2 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 21 Oct 2022 13:48:41 +0200 Subject: [PATCH 051/524] `ref_option_ref` do not lint when inner reference is mutable As it makes the `Option` Non Copy --- clippy_lints/src/ref_option_ref.rs | 3 ++- tests/ui/ref_option_ref.rs | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 42514f861be1..f21b3ea6c3b0 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -52,7 +52,8 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { GenericArg::Type(inner_ty) => Some(inner_ty), _ => None, }); - if let TyKind::Rptr(_, _) = inner_ty.kind; + if let TyKind::Rptr(_, ref inner_mut_ty) = inner_ty.kind; + if inner_mut_ty.mutbl == Mutability::Not; then { span_lint_and_sugg( diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 2df45c927d71..e487799e1522 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -45,3 +45,8 @@ impl RefOptTrait for u32 { fn main() { let x: &Option<&u32> = &None; } + +fn issue9682(arg: &Option<&mut String>) { + // Should not lint, as the inner ref is mutable making it non `Copy` + println!("{arg:?}"); +} From 487c6fc9adc02e835fde35451f016296450b1c33 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 21 Oct 2022 14:51:13 +0200 Subject: [PATCH 052/524] [`collapsible_match`] specify field name when destructuring structs --- clippy_lints/src/matches/collapsible_match.rs | 23 ++++++++++--- tests/ui/collapsible_match.rs | 21 ++++++++++++ tests/ui/collapsible_match.stderr | 34 ++++++++++++++++++- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index fd14d868df34..33a052c41a38 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, @@ -63,7 +64,8 @@ fn check_arm<'tcx>( if !pat_contains_or(inner_then_pat); // the binding must come from the pattern of the containing match arm // .... => match { .. } - if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); + if let (Some(binding_span), is_innermost_parent_pat_struct) + = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id); // the "else" branches must be equal if match (outer_else_body, inner_else_body) { (None, None) => true, @@ -88,6 +90,13 @@ fn check_arm<'tcx>( if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, if outer_is_match { "match" } else { "if let" }, ); + // collapsing patterns need an explicit field name in struct pattern matching + // ex: Struct {x: Some(1)} + let replace_msg = if is_innermost_parent_pat_struct { + format!(", prefixed by {}:", snippet(cx, binding_span, "their field name")) + } else { + String::new() + }; span_lint_and_then( cx, COLLAPSIBLE_MATCH, @@ -96,7 +105,7 @@ fn check_arm<'tcx>( |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); - help_span.push_span_label(inner_then_pat.span, "with this pattern"); + help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); @@ -117,8 +126,9 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } } -fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { +fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option, bool) { let mut span = None; + let mut is_innermost_parent_pat_struct = false; pat.walk_short(|p| match &p.kind { // ignore OR patterns PatKind::Or(_) => false, @@ -129,9 +139,12 @@ fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { } !found }, - _ => true, + _ => { + is_innermost_parent_pat_struct = matches!(p.kind, PatKind::Struct(..)); + true + }, }); - span + (span, is_innermost_parent_pat_struct) } fn pat_contains_or(pat: &Pat<'_>) -> bool { diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 7d53e08345d3..1d7a72846419 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -253,6 +253,27 @@ fn negative_cases(res_opt: Result, String>, res_res: Result>, b: () }, + B, +} + +pub fn test_1(x: Issue9647) { + if let Issue9647::A { a, .. } = x { + if let Some(u) = a { + println!("{u:?}") + } + } +} + +pub fn test_2(x: Issue9647) { + if let Issue9647::A { a: Some(a), .. } = x { + if let Some(u) = a { + println!("{u}") + } + } +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 2580bef58091..0294be60b43f 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -175,5 +175,37 @@ LL | Some(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: aborting due to 10 previous errors +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:263:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u:?}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:262:27 + | +LL | if let Issue9647::A { a, .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern, prefixed by a: + +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:271:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:270:35 + | +LL | if let Issue9647::A { a: Some(a), .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern + +error: aborting due to 12 previous errors From 655175494574265257f52f4f7a1e7eabd85cbe77 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 21 Oct 2022 15:27:25 +0200 Subject: [PATCH 053/524] [`unwrap_used`], [`expect_used`] do not lint in `test` cfg --- clippy_lints/src/methods/expect_used.rs | 4 ++-- clippy_lints/src/methods/unwrap_used.rs | 4 ++-- clippy_lints/src/utils/conf.rs | 4 ++-- tests/ui-toml/expect_used/expect_used.rs | 23 ++++++++++++-------- tests/ui-toml/unwrap_used/unwrap_used.rs | 21 ++++++++++++++---- tests/ui-toml/unwrap_used/unwrap_used.stderr | 6 ++--- 6 files changed, 40 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index d59fefa1ddc0..0d3c89280465 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_in_test_function; +use clippy_utils::is_in_cfg_test; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -27,7 +27,7 @@ pub(super) fn check( let method = if is_err { "expect_err" } else { "expect" }; - if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_expect_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index ee17f2d7889e..d11830a24b6c 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_in_test_function, is_lint_allowed}; +use clippy_utils::{is_in_cfg_test, is_lint_allowed}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -27,7 +27,7 @@ pub(super) fn check( let method_suffix = if is_err { "_err" } else { "" }; - if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_unwrap_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 668123e4d6e3..199dfafebf35 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -373,11 +373,11 @@ define_Conf! { (max_include_file_size: u64 = 1_000_000), /// Lint: EXPECT_USED. /// - /// Whether `expect` should be allowed in test functions + /// Whether `expect` should be allowed in test cfg (allow_expect_in_tests: bool = false), /// Lint: UNWRAP_USED. /// - /// Whether `unwrap` should be allowed in test functions + /// Whether `unwrap` should be allowed in test cfg (allow_unwrap_in_tests: bool = false), /// Lint: DBG_MACRO. /// diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs index 22dcd3ae9d69..bff97d97df77 100644 --- a/tests/ui-toml/expect_used/expect_used.rs +++ b/tests/ui-toml/expect_used/expect_used.rs @@ -16,14 +16,19 @@ fn main() { expect_result(); } -#[test] -fn test_expect_option() { - let opt = Some(0); - let _ = opt.expect(""); -} +#[cfg(test)] +mod issue9612 { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + let _a: u8 = 2.try_into().unwrap(); + let _a: u8 = 3.try_into().expect(""); -#[test] -fn test_expect_result() { - let res: Result = Ok(0); - let _ = res.expect(""); + util(); + } + + fn util() { + let _a: u8 = 4.try_into().unwrap(); + let _a: u8 = 5.try_into().expect(""); + } } diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs index 0e82fb20e455..bc8e8c1f0703 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -66,8 +66,21 @@ fn main() { } } -#[test] -fn test() { - let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); - let _ = boxed_slice.get(1).unwrap(); +#[cfg(test)] +mod issue9612 { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + let _a: u8 = 2.try_into().unwrap(); + let _a: u8 = 3.try_into().expect(""); + + util(); + } + + fn util() { + let _a: u8 = 4.try_into().unwrap(); + let _a: u8 = 5.try_into().expect(""); + // should still warn + let _ = Box::new([0]).get(1).unwrap(); + } } diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr index 681b5eaf54db..2bca88660e1c 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -188,10 +188,10 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:72:13 + --> $DIR/unwrap_used.rs:84:17 | -LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` +LL | let _ = Box::new([0]).get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&Box::new([0])[1]` error: aborting due to 27 previous errors From 815876d93f75c4d20c52cdd32f1a521ce306be63 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 21 Oct 2022 21:35:39 +0000 Subject: [PATCH 054/524] Move MSRV tests into the lint specific test files --- book/src/development/adding_lints.md | 23 +- tests/ui/cast_abs_to_unsigned.fixed | 18 +- tests/ui/cast_abs_to_unsigned.rs | 18 +- tests/ui/cast_abs_to_unsigned.stderr | 42 +-- tests/ui/cast_lossless_bool.fixed | 13 + tests/ui/cast_lossless_bool.rs | 13 + tests/ui/cast_lossless_bool.stderr | 34 ++- tests/ui/cfg_attr_rustfmt.fixed | 16 +- tests/ui/cfg_attr_rustfmt.rs | 16 +- tests/ui/cfg_attr_rustfmt.stderr | 8 +- tests/ui/checked_conversions.fixed | 16 ++ tests/ui/checked_conversions.rs | 16 ++ tests/ui/checked_conversions.stderr | 40 +-- tests/ui/cloned_instead_of_copied.fixed | 24 ++ tests/ui/cloned_instead_of_copied.rs | 24 ++ tests/ui/cloned_instead_of_copied.stderr | 30 ++- tests/ui/err_expect.fixed | 17 ++ tests/ui/err_expect.rs | 17 ++ tests/ui/err_expect.stderr | 10 +- tests/ui/filter_map_next_fixable.fixed | 16 ++ tests/ui/filter_map_next_fixable.rs | 16 ++ tests/ui/filter_map_next_fixable.stderr | 10 +- tests/ui/from_over_into.fixed | 25 ++ tests/ui/from_over_into.rs | 25 ++ tests/ui/from_over_into.stderr | 23 +- tests/ui/manual_clamp.rs | 27 ++ tests/ui/manual_clamp.stderr | 85 ++++--- tests/ui/manual_rem_euclid.fixed | 30 +++ tests/ui/manual_rem_euclid.rs | 30 +++ tests/ui/manual_rem_euclid.stderr | 30 ++- tests/ui/manual_strip.rs | 19 ++ tests/ui/manual_strip.stderr | 47 ++-- tests/ui/map_unwrap_or.rs | 20 +- tests/ui/map_unwrap_or.stderr | 30 ++- tests/ui/match_expr_like_matches_macro.fixed | 16 ++ tests/ui/match_expr_like_matches_macro.rs | 19 ++ tests/ui/match_expr_like_matches_macro.stderr | 38 ++- tests/ui/mem_replace.fixed | 18 +- tests/ui/mem_replace.rs | 18 +- tests/ui/mem_replace.stderr | 46 ++-- tests/ui/min_rust_version_attr.rs | 239 +----------------- tests/ui/min_rust_version_attr.stderr | 46 ++-- tests/ui/min_rust_version_invalid_attr.rs | 14 + tests/ui/min_rust_version_invalid_attr.stderr | 44 +++- .../min_rust_version_multiple_inner_attr.rs | 11 - ...in_rust_version_multiple_inner_attr.stderr | 38 --- tests/ui/min_rust_version_no_patch.rs | 14 - tests/ui/min_rust_version_outer_attr.rs | 4 - tests/ui/min_rust_version_outer_attr.stderr | 8 - .../ui/missing_const_for_fn/could_be_const.rs | 12 + .../could_be_const.stderr | 12 +- tests/ui/option_as_ref_deref.fixed | 17 +- tests/ui/option_as_ref_deref.rs | 17 +- tests/ui/option_as_ref_deref.stderr | 42 +-- tests/ui/range_contains.fixed | 26 +- tests/ui/range_contains.rs | 26 +- tests/ui/range_contains.stderr | 48 ++-- tests/ui/redundant_field_names.fixed | 16 ++ tests/ui/redundant_field_names.rs | 16 ++ tests/ui/redundant_field_names.stderr | 22 +- tests/ui/redundant_static_lifetimes.fixed | 13 + tests/ui/redundant_static_lifetimes.rs | 13 + tests/ui/redundant_static_lifetimes.stderr | 40 +-- tests/ui/unnested_or_patterns.fixed | 16 +- tests/ui/unnested_or_patterns.rs | 16 +- tests/ui/unnested_or_patterns.stderr | 13 +- tests/ui/use_self.fixed | 33 +++ tests/ui/use_self.rs | 33 +++ tests/ui/use_self.stderr | 90 ++++--- 69 files changed, 1290 insertions(+), 632 deletions(-) delete mode 100644 tests/ui/min_rust_version_multiple_inner_attr.rs delete mode 100644 tests/ui/min_rust_version_multiple_inner_attr.stderr delete mode 100644 tests/ui/min_rust_version_no_patch.rs delete mode 100644 tests/ui/min_rust_version_outer_attr.rs delete mode 100644 tests/ui/min_rust_version_outer_attr.stderr diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index b1e843bc7f4c..3c3f368a529b 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -478,8 +478,27 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { ``` Once the `msrv` is added to the lint, a relevant test case should be added to -`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted -if the project's MSRV is lower. +the lint's test file, `tests/ui/manual_strip.rs` in this example. It should +have a case for the version below the MSRV and one with the same contents but +for the MSRV version itself. + +```rust +#![feature(custom_inner_attributes)] + +... + +fn msrv_1_44() { + #![clippy::msrv = "1.44"] + + /* something that would trigger the lint */ +} + +fn msrv_1_45() { + #![clippy::msrv = "1.45"] + + /* something that would trigger the lint */ +} +``` As a last step, the lint should be added to the lint documentation. This is done in `clippy_lints/src/utils/conf.rs`: diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index a37f3fec20f1..e6bf944c7a5e 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,6 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +32,17 @@ fn main() { let _ = (x as i64 - y as i64).unsigned_abs() as u32; } + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + assert_eq!(10u32, x.unsigned_abs()); +} diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 5706930af5a0..c87320b5209d 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,6 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +32,17 @@ fn main() { let _ = (x as i64 - y as i64).abs() as u32; } + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 7cea11c183d2..1b39c554b038 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:7:18 + --> $DIR/cast_abs_to_unsigned.rs:9:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,100 +7,106 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:11:20 + --> $DIR/cast_abs_to_unsigned.rs:13:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:12:20 + --> $DIR/cast_abs_to_unsigned.rs:14:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:13:13 + --> $DIR/cast_abs_to_unsigned.rs:15:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:16:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:17:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:22:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:21:13 + --> $DIR/cast_abs_to_unsigned.rs:23:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:24:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:25:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:30:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:29:13 + --> $DIR/cast_abs_to_unsigned.rs:31:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:31:13 + --> $DIR/cast_abs_to_unsigned.rs:33:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` -error: aborting due to 17 previous errors +error: casting the result of `i32::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:47:23 + | +LL | assert_eq!(10u32, x.abs() as u32); + | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/cast_lossless_bool.fixed b/tests/ui/cast_lossless_bool.fixed index 9e2da45c3785..af13b755e310 100644 --- a/tests/ui/cast_lossless_bool.fixed +++ b/tests/ui/cast_lossless_bool.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -40,3 +41,15 @@ mod cast_lossless_in_impl { } } } + +fn msrv_1_27() { + #![clippy::msrv = "1.27"] + + let _ = true as u8; +} + +fn msrv_1_28() { + #![clippy::msrv = "1.28"] + + let _ = u8::from(true); +} diff --git a/tests/ui/cast_lossless_bool.rs b/tests/ui/cast_lossless_bool.rs index b6f6c59a01f9..3b06af899c60 100644 --- a/tests/ui/cast_lossless_bool.rs +++ b/tests/ui/cast_lossless_bool.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -40,3 +41,15 @@ mod cast_lossless_in_impl { } } } + +fn msrv_1_27() { + #![clippy::msrv = "1.27"] + + let _ = true as u8; +} + +fn msrv_1_28() { + #![clippy::msrv = "1.28"] + + let _ = true as u8; +} diff --git a/tests/ui/cast_lossless_bool.stderr b/tests/ui/cast_lossless_bool.stderr index 6b148336011d..768b033d10a2 100644 --- a/tests/ui/cast_lossless_bool.stderr +++ b/tests/ui/cast_lossless_bool.stderr @@ -1,5 +1,5 @@ error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> $DIR/cast_lossless_bool.rs:8:13 + --> $DIR/cast_lossless_bool.rs:9:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` @@ -7,76 +7,82 @@ LL | let _ = true as u8; = note: `-D clippy::cast-lossless` implied by `-D warnings` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:9:13 + --> $DIR/cast_lossless_bool.rs:10:13 | LL | let _ = true as u16; | ^^^^^^^^^^^ help: try: `u16::from(true)` error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)` - --> $DIR/cast_lossless_bool.rs:10:13 + --> $DIR/cast_lossless_bool.rs:11:13 | LL | let _ = true as u32; | ^^^^^^^^^^^ help: try: `u32::from(true)` error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)` - --> $DIR/cast_lossless_bool.rs:11:13 + --> $DIR/cast_lossless_bool.rs:12:13 | LL | let _ = true as u64; | ^^^^^^^^^^^ help: try: `u64::from(true)` error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)` - --> $DIR/cast_lossless_bool.rs:12:13 + --> $DIR/cast_lossless_bool.rs:13:13 | LL | let _ = true as u128; | ^^^^^^^^^^^^ help: try: `u128::from(true)` error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)` - --> $DIR/cast_lossless_bool.rs:13:13 + --> $DIR/cast_lossless_bool.rs:14:13 | LL | let _ = true as usize; | ^^^^^^^^^^^^^ help: try: `usize::from(true)` error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)` - --> $DIR/cast_lossless_bool.rs:15:13 + --> $DIR/cast_lossless_bool.rs:16:13 | LL | let _ = true as i8; | ^^^^^^^^^^ help: try: `i8::from(true)` error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)` - --> $DIR/cast_lossless_bool.rs:16:13 + --> $DIR/cast_lossless_bool.rs:17:13 | LL | let _ = true as i16; | ^^^^^^^^^^^ help: try: `i16::from(true)` error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)` - --> $DIR/cast_lossless_bool.rs:17:13 + --> $DIR/cast_lossless_bool.rs:18:13 | LL | let _ = true as i32; | ^^^^^^^^^^^ help: try: `i32::from(true)` error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)` - --> $DIR/cast_lossless_bool.rs:18:13 + --> $DIR/cast_lossless_bool.rs:19:13 | LL | let _ = true as i64; | ^^^^^^^^^^^ help: try: `i64::from(true)` error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)` - --> $DIR/cast_lossless_bool.rs:19:13 + --> $DIR/cast_lossless_bool.rs:20:13 | LL | let _ = true as i128; | ^^^^^^^^^^^^ help: try: `i128::from(true)` error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)` - --> $DIR/cast_lossless_bool.rs:20:13 + --> $DIR/cast_lossless_bool.rs:21:13 | LL | let _ = true as isize; | ^^^^^^^^^^^^^ help: try: `isize::from(true)` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:23:13 + --> $DIR/cast_lossless_bool.rs:24:13 | LL | let _ = (true | false) as u16; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` -error: aborting due to 13 previous errors +error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` + --> $DIR/cast_lossless_bool.rs:54:13 + | +LL | let _ = true as u8; + | ^^^^^^^^^^ help: try: `u8::from(true)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/cfg_attr_rustfmt.fixed b/tests/ui/cfg_attr_rustfmt.fixed index 061a4ab9b2ef..8a5645b22ed1 100644 --- a/tests/ui/cfg_attr_rustfmt.fixed +++ b/tests/ui/cfg_attr_rustfmt.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, custom_inner_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -29,3 +29,17 @@ mod foo { pub fn f() {} } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + #[rustfmt::skip] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.rs b/tests/ui/cfg_attr_rustfmt.rs index 035169fab85b..2fb140efae76 100644 --- a/tests/ui/cfg_attr_rustfmt.rs +++ b/tests/ui/cfg_attr_rustfmt.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, custom_inner_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -29,3 +29,17 @@ mod foo { pub fn f() {} } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.stderr b/tests/ui/cfg_attr_rustfmt.stderr index c1efd47db90b..08df7b2b39a0 100644 --- a/tests/ui/cfg_attr_rustfmt.stderr +++ b/tests/ui/cfg_attr_rustfmt.stderr @@ -12,5 +12,11 @@ error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes LL | #[cfg_attr(rustfmt, rustfmt_skip)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` -error: aborting due to 2 previous errors +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes + --> $DIR/cfg_attr_rustfmt.rs:43:5 + | +LL | #[cfg_attr(rustfmt, rustfmt::skip)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` + +error: aborting due to 3 previous errors diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index cb7100bc9efa..f936957cb40c 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -1,7 +1,9 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +78,18 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +fn msrv_1_33() { + #![clippy::msrv = "1.33"] + + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let value: i64 = 34; + let _ = u32::try_from(value).is_ok(); +} + fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index ed4e0692388a..77aec713ff31 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -1,7 +1,9 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +78,18 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +fn msrv_1_33() { + #![clippy::msrv = "1.33"] + + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let value: i64 = 34; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + fn main() {} diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 2e518040561c..b2bf7af8daf8 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:15:13 + --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,94 +7,100 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = note: `-D clippy::checked-conversions` implied by `-D warnings` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:16:13 + --> $DIR/checked_conversions.rs:18:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:20:13 + --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:21:13 + --> $DIR/checked_conversions.rs:23:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:25:13 + --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:26:13 + --> $DIR/checked_conversions.rs:28:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:32:13 + --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:33:13 + --> $DIR/checked_conversions.rs:35:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:37:13 + --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:38:13 + --> $DIR/checked_conversions.rs:40:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:44:13 + --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:45:13 + --> $DIR/checked_conversions.rs:47:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:49:13 + --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:50:13 + --> $DIR/checked_conversions.rs:52:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:54:13 + --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:55:13 + --> $DIR/checked_conversions.rs:57:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: aborting due to 16 previous errors +error: checked cast can be simplified + --> $DIR/checked_conversions.rs:92:13 + | +LL | let _ = value <= (u32::MAX as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` + +error: aborting due to 17 previous errors diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed index 4eb999e18e64..42ed232d1001 100644 --- a/tests/ui/cloned_instead_of_copied.fixed +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -1,5 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +16,24 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).copied(); // Option::copied needs 1.35 +} + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + let _ = [1].iter().copied(); // Iterator::copied needs 1.36 + let _ = Some(&1).copied(); +} diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs index 894496c0ebbb..471bd9654cc1 100644 --- a/tests/ui/cloned_instead_of_copied.rs +++ b/tests/ui/cloned_instead_of_copied.rs @@ -1,5 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +16,24 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); // Option::copied needs 1.35 +} + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + let _ = Some(&1).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr index e0707d321468..914c9a91e830 100644 --- a/tests/ui/cloned_instead_of_copied.stderr +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -1,5 +1,5 @@ error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:6:24 + --> $DIR/cloned_instead_of_copied.rs:9:24 | LL | let _ = [1].iter().cloned(); | ^^^^^^ help: try: `copied` @@ -7,28 +7,46 @@ LL | let _ = [1].iter().cloned(); = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:7:31 + --> $DIR/cloned_instead_of_copied.rs:10:31 | LL | let _ = vec!["hi"].iter().cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:8:22 + --> $DIR/cloned_instead_of_copied.rs:11:22 | LL | let _ = Some(&1).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:9:34 + --> $DIR/cloned_instead_of_copied.rs:12:34 | LL | let _ = Box::new([1].iter()).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:10:32 + --> $DIR/cloned_instead_of_copied.rs:13:32 | LL | let _ = Box::new(Some(&1)).cloned(); | ^^^^^^ help: try: `copied` -error: aborting due to 5 previous errors +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:31:22 + | +LL | let _ = Some(&1).cloned(); // Option::copied needs 1.35 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:37:24 + | +LL | let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:38:22 + | +LL | let _ = Some(&1).cloned(); + | ^^^^^^ help: try: `copied` + +error: aborting due to 8 previous errors diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed index 7e18d70bae40..3bac738acd65 100644 --- a/tests/ui/err_expect.fixed +++ b/tests/ui/err_expect.fixed @@ -1,5 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +15,17 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let x: Result = Ok(16); + x.err().expect("16"); +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let x: Result = Ok(17); + x.expect_err("17"); +} diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs index bf8c3c9fb8c9..6e7c47d9ad3c 100644 --- a/tests/ui/err_expect.rs +++ b/tests/ui/err_expect.rs @@ -1,5 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +15,17 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let x: Result = Ok(16); + x.err().expect("16"); +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let x: Result = Ok(17); + x.err().expect("17"); +} diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr index ffd97e00a5c0..91a6cf8de65f 100644 --- a/tests/ui/err_expect.stderr +++ b/tests/ui/err_expect.stderr @@ -1,10 +1,16 @@ error: called `.err().expect()` on a `Result` value - --> $DIR/err_expect.rs:10:16 + --> $DIR/err_expect.rs:13:16 | LL | test_debug.err().expect("Testing debug type"); | ^^^^^^^^^^^^ help: try: `expect_err` | = note: `-D clippy::err-expect` implied by `-D warnings` -error: aborting due to previous error +error: called `.err().expect()` on a `Result` value + --> $DIR/err_expect.rs:30:7 + | +LL | x.err().expect("17"); + | ^^^^^^^^^^^^ help: try: `expect_err` + +error: aborting due to 2 previous errors diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed index c3992d7e92cf..41828ddd7acd 100644 --- a/tests/ui/filter_map_next_fixable.fixed +++ b/tests/ui/filter_map_next_fixable.fixed @@ -1,6 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +10,17 @@ fn main() { let element: Option = a.iter().find_map(|s| s.parse().ok()); assert_eq!(element, Some(1)); } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().find_map(|s| s.parse().ok()); +} diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs index 447219a96839..be492a81b45e 100644 --- a/tests/ui/filter_map_next_fixable.rs +++ b/tests/ui/filter_map_next_fixable.rs @@ -1,6 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +10,17 @@ fn main() { let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); assert_eq!(element, Some(1)); } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr index 3bb062ffd7a3..e789efeabd55 100644 --- a/tests/ui/filter_map_next_fixable.stderr +++ b/tests/ui/filter_map_next_fixable.stderr @@ -1,10 +1,16 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> $DIR/filter_map_next_fixable.rs:8:32 + --> $DIR/filter_map_next_fixable.rs:10:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` | = note: `-D clippy::filter-map-next` implied by `-D warnings` -error: aborting due to previous error +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead + --> $DIR/filter_map_next_fixable.rs:25:26 + | +LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` + +error: aborting due to 2 previous errors diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed index e66dc43b0473..1cf49ca45f49 100644 --- a/tests/ui/from_over_into.fixed +++ b/tests/ui/from_over_into.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -59,4 +60,28 @@ impl From for A { } } +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + struct FromOverInto(Vec); + + impl From> for FromOverInto { + fn from(val: Vec) -> Self { + FromOverInto(val) + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 74c7be6af79e..d30f3c3fc925 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -59,4 +60,28 @@ impl From for A { } } +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 6cf83e258071..9c2a7c04c364 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,5 +1,5 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:9:1 + --> $DIR/from_over_into.rs:10:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL ~ StringWrapper(val) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:17:1 + --> $DIR/from_over_into.rs:18:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL ~ SelfType(String::new()) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:32:1 + --> $DIR/from_over_into.rs:33:1 | LL | impl Into for X { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL ~ let _: X = val; | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:44:1 + --> $DIR/from_over_into.rs:45:1 | LL | impl core::convert::Into for crate::ExplicitPaths { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,5 +58,18 @@ LL ~ val.0 = false; LL ~ val.0 | -error: aborting due to 4 previous errors +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:80:5 + | +LL | impl Into> for Vec { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From>` + | +LL ~ impl From> for FromOverInto { +LL ~ fn from(val: Vec) -> Self { +LL ~ FromOverInto(val) + | + +error: aborting due to 5 previous errors diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs index 54fd888af99f..331fd29b74e8 100644 --- a/tests/ui/manual_clamp.rs +++ b/tests/ui/manual_clamp.rs @@ -1,3 +1,4 @@ +#![feature(custom_inner_attributes)] #![warn(clippy::manual_clamp)] #![allow( unused, @@ -302,3 +303,29 @@ fn dont_tell_me_what_to_do() { fn cmp_min_max(input: i32) -> i32 { input * 3 } + +fn msrv_1_49() { + #![clippy::msrv = "1.49"] + + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr index 0604f8606c3f..70abe28091c9 100644 --- a/tests/ui/manual_clamp.stderr +++ b/tests/ui/manual_clamp.stderr @@ -1,5 +1,5 @@ error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:76:5 + --> $DIR/manual_clamp.rs:77:5 | LL | / if x9 < min { LL | | x9 = min; @@ -13,7 +13,7 @@ LL | | } = note: `-D clippy::manual-clamp` implied by `-D warnings` error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:91:5 + --> $DIR/manual_clamp.rs:92:5 | LL | / if x11 > max { LL | | x11 = max; @@ -26,7 +26,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:99:5 + --> $DIR/manual_clamp.rs:100:5 | LL | / if min > x12 { LL | | x12 = min; @@ -39,7 +39,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:107:5 + --> $DIR/manual_clamp.rs:108:5 | LL | / if max < x13 { LL | | x13 = max; @@ -52,7 +52,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:161:5 + --> $DIR/manual_clamp.rs:162:5 | LL | / if max < x33 { LL | | x33 = max; @@ -65,7 +65,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:21:14 + --> $DIR/manual_clamp.rs:22:14 | LL | let x0 = if max < input { | ______________^ @@ -80,7 +80,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:29:14 + --> $DIR/manual_clamp.rs:30:14 | LL | let x1 = if input > max { | ______________^ @@ -95,7 +95,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:37:14 + --> $DIR/manual_clamp.rs:38:14 | LL | let x2 = if input < min { | ______________^ @@ -110,7 +110,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:45:14 + --> $DIR/manual_clamp.rs:46:14 | LL | let x3 = if min > input { | ______________^ @@ -125,7 +125,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:53:14 + --> $DIR/manual_clamp.rs:54:14 | LL | let x4 = input.max(min).min(max); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -133,7 +133,7 @@ LL | let x4 = input.max(min).min(max); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:55:14 + --> $DIR/manual_clamp.rs:56:14 | LL | let x5 = input.min(max).max(min); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -141,7 +141,7 @@ LL | let x5 = input.min(max).max(min); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:57:14 + --> $DIR/manual_clamp.rs:58:14 | LL | let x6 = match input { | ______________^ @@ -154,7 +154,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:63:14 + --> $DIR/manual_clamp.rs:64:14 | LL | let x7 = match input { | ______________^ @@ -167,7 +167,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:69:14 + --> $DIR/manual_clamp.rs:70:14 | LL | let x8 = match input { | ______________^ @@ -180,7 +180,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:83:15 + --> $DIR/manual_clamp.rs:84:15 | LL | let x10 = match input { | _______________^ @@ -193,7 +193,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:114:15 + --> $DIR/manual_clamp.rs:115:15 | LL | let x14 = if input > CONST_MAX { | _______________^ @@ -208,7 +208,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:123:19 + --> $DIR/manual_clamp.rs:124:19 | LL | let x15 = if input > max { | ___________________^ @@ -224,7 +224,7 @@ LL | | }; = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:134:19 + --> $DIR/manual_clamp.rs:135:19 | LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -232,7 +232,7 @@ LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:135:19 + --> $DIR/manual_clamp.rs:136:19 | LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -240,7 +240,7 @@ LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:136:19 + --> $DIR/manual_clamp.rs:137:19 | LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -248,7 +248,7 @@ LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:137:19 + --> $DIR/manual_clamp.rs:138:19 | LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -256,7 +256,7 @@ LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:138:19 + --> $DIR/manual_clamp.rs:139:19 | LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -264,7 +264,7 @@ LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:139:19 + --> $DIR/manual_clamp.rs:140:19 | LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -272,7 +272,7 @@ LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:140:19 + --> $DIR/manual_clamp.rs:141:19 | LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -280,7 +280,7 @@ LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:141:19 + --> $DIR/manual_clamp.rs:142:19 | LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -288,7 +288,7 @@ LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:143:19 + --> $DIR/manual_clamp.rs:144:19 | LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -297,7 +297,7 @@ LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:144:19 + --> $DIR/manual_clamp.rs:145:19 | LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -306,7 +306,7 @@ LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:145:19 + --> $DIR/manual_clamp.rs:146:19 | LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -315,7 +315,7 @@ LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:146:19 + --> $DIR/manual_clamp.rs:147:19 | LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -324,7 +324,7 @@ LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:147:19 + --> $DIR/manual_clamp.rs:148:19 | LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -333,7 +333,7 @@ LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:148:19 + --> $DIR/manual_clamp.rs:149:19 | LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -342,7 +342,7 @@ LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:149:19 + --> $DIR/manual_clamp.rs:150:19 | LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -351,7 +351,7 @@ LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:150:19 + --> $DIR/manual_clamp.rs:151:19 | LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -360,7 +360,7 @@ LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:153:5 + --> $DIR/manual_clamp.rs:154:5 | LL | / if x32 < min { LL | | x32 = min; @@ -371,5 +371,20 @@ LL | | } | = note: clamp will panic if max < min -error: aborting due to 34 previous errors +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:324:13 + | +LL | let _ = if input < min { + | _____________^ +LL | | min +LL | | } else if input > max { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: aborting due to 35 previous errors diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed index 5601c96c10b2..b942fbfe9305 100644 --- a/tests/ui/manual_rem_euclid.fixed +++ b/tests/ui/manual_rem_euclid.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -53,3 +54,32 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { num.rem_euclid(4) } + +pub fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub fn msrv_1_38() { + #![clippy::msrv = "1.38"] + + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} + +// For const fns: +pub const fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub const fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs index 52135be26b73..7462d532169f 100644 --- a/tests/ui/manual_rem_euclid.rs +++ b/tests/ui/manual_rem_euclid.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -53,3 +54,32 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { ((num % 4) + 4) % 4 } + +pub fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub fn msrv_1_38() { + #![clippy::msrv = "1.38"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +// For const fns: +pub const fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub const fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr index a237fd0213c1..d51bac03b565 100644 --- a/tests/ui/manual_rem_euclid.stderr +++ b/tests/ui/manual_rem_euclid.stderr @@ -1,5 +1,5 @@ error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:19:18 + --> $DIR/manual_rem_euclid.rs:20:18 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -7,31 +7,31 @@ LL | let _: i32 = ((value % 4) + 4) % 4; = note: `-D clippy::manual-rem-euclid` implied by `-D warnings` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:20:18 + --> $DIR/manual_rem_euclid.rs:21:18 | LL | let _: i32 = (4 + (value % 4)) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:21:18 + --> $DIR/manual_rem_euclid.rs:22:18 | LL | let _: i32 = (value % 4 + 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:22:18 + --> $DIR/manual_rem_euclid.rs:23:18 | LL | let _: i32 = (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:23:22 + --> $DIR/manual_rem_euclid.rs:24:22 | LL | let _: i32 = 1 + (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:12:22 + --> $DIR/manual_rem_euclid.rs:13:22 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -42,16 +42,28 @@ LL | internal_rem_euclid!(); = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:49:5 + --> $DIR/manual_rem_euclid.rs:50:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:54:5 + --> $DIR/manual_rem_euclid.rs:55:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` -error: aborting due to 8 previous errors +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:69:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:84:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index cbb84eb5c7e3..85009d78558b 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -1,3 +1,4 @@ +#![feature(custom_inner_attributes)] #![warn(clippy::manual_strip)] fn main() { @@ -64,3 +65,21 @@ fn main() { s4[2..].to_string(); } } + +fn msrv_1_44() { + #![clippy::msrv = "1.44"] + + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} + +fn msrv_1_45() { + #![clippy::msrv = "1.45"] + + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index 2191ccb85dd5..ad2a362f3e76 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> $DIR/manual_strip.rs:7:24 + --> $DIR/manual_strip.rs:8:24 | LL | str::to_string(&s["ab".len()..]); | ^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:6:5 + --> $DIR/manual_strip.rs:7:5 | LL | if s.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL ~ .to_string(); | error: stripping a suffix manually - --> $DIR/manual_strip.rs:15:24 + --> $DIR/manual_strip.rs:16:24 | LL | str::to_string(&s[..s.len() - "bc".len()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the suffix was tested here - --> $DIR/manual_strip.rs:14:5 + --> $DIR/manual_strip.rs:15:5 | LL | if s.ends_with("bc") { | ^^^^^^^^^^^^^^^^^^^^^ @@ -42,13 +42,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:24:24 + --> $DIR/manual_strip.rs:25:24 | LL | str::to_string(&s[1..]); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:23:5 + --> $DIR/manual_strip.rs:24:5 | LL | if s.starts_with('a') { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -60,13 +60,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:31:24 + --> $DIR/manual_strip.rs:32:24 | LL | str::to_string(&s[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:30:5 + --> $DIR/manual_strip.rs:31:5 | LL | if s.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,13 +77,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:37:24 + --> $DIR/manual_strip.rs:38:24 | LL | str::to_string(&s[PREFIX.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:36:5 + --> $DIR/manual_strip.rs:37:5 | LL | if s.starts_with(PREFIX) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,13 +95,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:44:24 + --> $DIR/manual_strip.rs:45:24 | LL | str::to_string(&TARGET[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:43:5 + --> $DIR/manual_strip.rs:44:5 | LL | if TARGET.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,13 +112,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:50:9 + --> $DIR/manual_strip.rs:51:9 | LL | s1[2..].to_uppercase(); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:49:5 + --> $DIR/manual_strip.rs:50:5 | LL | if s1.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,5 +128,22 @@ LL ~ if let Some() = s1.strip_prefix("ab") { LL ~ .to_uppercase(); | -error: aborting due to 7 previous errors +error: stripping a prefix manually + --> $DIR/manual_strip.rs:83:9 + | +LL | s[1..].to_string(); + | ^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:82:5 + | +LL | if s.starts_with('a') { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL ~ if let Some() = s.strip_prefix('a') { +LL ~ .to_string(); + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 5429fb4e454e..396b22a9abb3 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,8 @@ // aux-build:option_helpers.rs + +#![feature(custom_inner_attributes)] #![warn(clippy::map_unwrap_or)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)] #[macro_use] extern crate option_helpers; @@ -79,3 +81,19 @@ fn main() { option_methods(); result_methods(); } + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index abc9c1ece327..d17d24a403ea 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,5 +1,5 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:16:13 + --> $DIR/map_unwrap_or.rs:18:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -15,7 +15,7 @@ LL + let _ = opt.map_or(0, |x| x + 1); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:20:13 + --> $DIR/map_unwrap_or.rs:22:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -33,7 +33,7 @@ LL ~ ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:24:13 + --> $DIR/map_unwrap_or.rs:26:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -50,7 +50,7 @@ LL ~ }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:29:13 + --> $DIR/map_unwrap_or.rs:31:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + let _ = opt.and_then(|x| Some(x + 1)); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:31:13 + --> $DIR/map_unwrap_or.rs:33:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -80,7 +80,7 @@ LL ~ ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:35:13 + --> $DIR/map_unwrap_or.rs:37:13 | LL | let _ = opt | _____________^ @@ -95,7 +95,7 @@ LL + .and_then(|x| Some(x + 1)); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:46:13 + --> $DIR/map_unwrap_or.rs:48:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:50:13 + --> $DIR/map_unwrap_or.rs:52:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -117,7 +117,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:54:13 + --> $DIR/map_unwrap_or.rs:56:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -127,7 +127,7 @@ LL | | ); | |_________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:66:13 + --> $DIR/map_unwrap_or.rs:68:13 | LL | let _ = res.map(|x| { | _____________^ @@ -137,7 +137,7 @@ LL | | ).unwrap_or_else(|_e| 0); | |____________________________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:70:13 + --> $DIR/map_unwrap_or.rs:72:13 | LL | let _ = res.map(|x| x + 1) | _____________^ @@ -146,5 +146,11 @@ LL | | 0 LL | | }); | |__________^ -error: aborting due to 11 previous errors +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:98:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: aborting due to 12 previous errors diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 95ca571d07bf..2498007694c5 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] @@ -193,3 +194,18 @@ fn main() { _ => false, }; } + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn msrv_1_42() { + #![clippy::msrv = "1.42"] + + let _y = matches!(Some(5), Some(0)); +} diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 3b9c8cadadcc..b4e48499bd0f 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] @@ -234,3 +235,21 @@ fn main() { _ => false, }; } + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn msrv_1_42() { + #![clippy::msrv = "1.42"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index e94555e27448..f1d1c23aeb0d 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:10:14 + --> $DIR/match_expr_like_matches_macro.rs:11:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:16:14 + --> $DIR/match_expr_like_matches_macro.rs:17:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:22:14 + --> $DIR/match_expr_like_matches_macro.rs:23:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:28:15 + --> $DIR/match_expr_like_matches_macro.rs:29:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:34:16 + --> $DIR/match_expr_like_matches_macro.rs:35:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:58:20 + --> $DIR/match_expr_like_matches_macro.rs:59:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:68:20 + --> $DIR/match_expr_like_matches_macro.rs:69:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:78:20 + --> $DIR/match_expr_like_matches_macro.rs:79:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:138:18 + --> $DIR/match_expr_like_matches_macro.rs:139:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:147:18 + --> $DIR/match_expr_like_matches_macro.rs:148:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:164:21 + --> $DIR/match_expr_like_matches_macro.rs:165:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:178:20 + --> $DIR/match_expr_like_matches_macro.rs:179:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:190:20 + --> $DIR/match_expr_like_matches_macro.rs:191:20 | LL | let _res = match &val { | ____________________^ @@ -133,5 +133,15 @@ LL | | _ => false, LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` -error: aborting due to 13 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:251:14 + | +LL | let _y = match Some(5) { + | ______________^ +LL | | Some(0) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(Some(5), Some(0))` + +error: aborting due to 14 previous errors diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index b609ba659467..ae237395b95f 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -1,5 +1,7 @@ // run-rustfix -#![allow(unused_imports)] + +#![feature(custom_inner_attributes)] +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +79,17 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let mut s = String::from("foo"); + let _ = std::mem::take(&mut s); +} diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 93f6dcdec83b..3202e99e0be9 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -1,5 +1,7 @@ // run-rustfix -#![allow(unused_imports)] + +#![feature(custom_inner_attributes)] +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +79,17 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index 90dc6c95f858..dd8a50dab900 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:15:13 + --> $DIR/mem_replace.rs:17:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:17:13 + --> $DIR/mem_replace.rs:19:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:22:13 + --> $DIR/mem_replace.rs:24:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,100 +21,106 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:25:13 + --> $DIR/mem_replace.rs:27:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:26:13 + --> $DIR/mem_replace.rs:28:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:29:13 + --> $DIR/mem_replace.rs:31:13 | LL | let _ = std::mem::replace(&mut v, Vec::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:30:13 + --> $DIR/mem_replace.rs:32:13 | LL | let _ = std::mem::replace(&mut v, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:31:13 + --> $DIR/mem_replace.rs:33:13 | LL | let _ = std::mem::replace(&mut v, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:32:13 + --> $DIR/mem_replace.rs:34:13 | LL | let _ = std::mem::replace(&mut v, vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:35:13 + --> $DIR/mem_replace.rs:37:13 | LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:38:13 + --> $DIR/mem_replace.rs:40:13 | LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:41:13 + --> $DIR/mem_replace.rs:43:13 | LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:44:13 + --> $DIR/mem_replace.rs:46:13 | LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:47:13 + --> $DIR/mem_replace.rs:49:13 | LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:50:13 + --> $DIR/mem_replace.rs:52:13 | LL | let _ = std::mem::replace(&mut list, LinkedList::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:53:13 + --> $DIR/mem_replace.rs:55:13 | LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:56:13 + --> $DIR/mem_replace.rs:58:13 | LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:59:13 + --> $DIR/mem_replace.rs:61:13 | LL | let _ = std::mem::replace(&mut refstr, ""); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:62:13 + --> $DIR/mem_replace.rs:64:13 | LL | let _ = std::mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` -error: aborting due to 19 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:94:13 + | +LL | let _ = std::mem::replace(&mut s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` + +error: aborting due to 20 previous errors diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index c4c6391bb4c1..cd148063bf06 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -1,240 +1,29 @@ #![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0.0"] -use std::ops::{Deref, RangeFrom}; +fn main() {} -fn approx_const() { +fn just_under_msrv() { + #![clippy::msrv = "1.42.0"] let log2_10 = 3.321928094887362; - let log10_2 = 0.301029995663981; } -fn cloned_instead_of_copied() { - let _ = [1].iter().cloned(); -} - -fn option_as_ref_deref() { - let mut opt = Some(String::from("123")); - - let _ = opt.as_ref().map(String::as_str); - let _ = opt.as_ref().map(|x| x.as_str()); - let _ = opt.as_mut().map(String::as_mut_str); - let _ = opt.as_mut().map(|x| x.as_mut_str()); -} - -fn match_like_matches() { - let _y = match Some(5) { - Some(0) => true, - _ => false, - }; -} - -fn match_same_arms() { - match (1, 2, 3) { - (1, .., 3) => 42, - (.., 3) => 42, //~ ERROR match arms have same body - _ => 0, - }; -} - -fn match_same_arms2() { - let _ = match Some(42) { - Some(_) => 24, - None => 24, //~ ERROR match arms have same body - }; -} - -pub fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -pub fn redundant_fieldnames() { - let start = 0; - let _ = RangeFrom { start: start }; -} - -pub fn redundant_static_lifetime() { - const VAR_ONE: &'static str = "Test constant #1"; -} - -pub fn checked_conversion() { - let value: i64 = 42; - let _ = value <= (u32::max_value() as i64) && value >= 0; - let _ = value <= (u32::MAX as i64) && value >= 0; -} - -pub struct FromOverInto(String); - -impl Into for String { - fn into(self) -> FromOverInto { - FromOverInto(self) - } -} - -pub fn filter_map_next() { - let a = ["1", "lol", "3", "NaN", "5"]; - - #[rustfmt::skip] - let _: Option = vec![1, 2, 3, 4, 5, 6] - .into_iter() - .filter_map(|x| { - if x == 2 { - Some(x * 2) - } else { - None - } - }) - .next(); -} - -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] -pub fn manual_range_contains() { - let x = 5; - x >= 8 && x < 12; -} - -pub fn use_self() { - struct Foo; - - impl Foo { - fn new() -> Foo { - Foo {} - } - fn test() -> Foo { - Foo::new() - } - } -} - -fn replace_with_default() { - let mut s = String::from("foo"); - let _ = std::mem::replace(&mut s, String::default()); -} - -fn map_unwrap_or() { - let opt = Some(1); - - // Check for `option.map(_).unwrap_or(_)` use. - // Single line case. - let _ = opt - .map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or(0); -} - -// Could be const -fn missing_const_for_fn() -> i32 { - 1 -} - -fn unnest_or_patterns() { - struct TS(u8, u8); - if let TS(0, x) | TS(1, x) = TS(0, 0) {} -} - -#[cfg_attr(rustfmt, rustfmt_skip)] -fn deprecated_cfg_attr() {} - -#[warn(clippy::cast_lossless)] -fn int_from_bool() -> u8 { - true as u8 -} - -fn err_expect() { - let x: Result = Ok(10); - x.err().expect("Testing expect_err"); -} - -fn cast_abs_to_unsigned() { - let x: i32 = 10; - assert_eq!(10u32, x.abs() as u32); -} - -fn manual_rem_euclid() { - let x: i32 = 10; - let _: i32 = ((x % 4) + 4) % 4; -} - -fn manual_clamp() { - let (input, min, max) = (0, -1, 2); - let _ = if input < min { - min - } else if input > max { - max - } else { - input - }; -} - -fn main() { - filter_map_next(); - checked_conversion(); - redundant_fieldnames(); - redundant_static_lifetime(); - option_as_ref_deref(); - match_like_matches(); - match_same_arms(); - match_same_arms2(); - manual_strip_msrv(); - manual_range_contains(); - use_self(); - replace_with_default(); - map_unwrap_or(); - missing_const_for_fn(); - unnest_or_patterns(); - int_from_bool(); - err_expect(); - cast_abs_to_unsigned(); - manual_rem_euclid(); - manual_clamp(); +fn meets_msrv() { + #![clippy::msrv = "1.43.0"] + let log2_10 = 3.321928094887362; } -mod just_under_msrv { - #![feature(custom_inner_attributes)] +fn just_above_msrv() { #![clippy::msrv = "1.44.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } -} - -mod meets_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.45.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } + let log2_10 = 3.321928094887362; } -mod just_above_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.46.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } +fn no_patch_under() { + #![clippy::msrv = "1.42"] + let log2_10 = 3.321928094887362; } -mod const_rem_euclid { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.50.0"] - - pub const fn const_rem_euclid_4(num: i32) -> i32 { - ((num % 4) + 4) % 4 - } +fn no_patch_meets() { + #![clippy::msrv = "1.43"] + let log2_10 = 3.321928094887362; } diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index d1cffc26a831..68aa58748190 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,37 +1,27 @@ -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:216:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:13:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:215:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `-D clippy::manual-strip` implied by `-D warnings` -help: try using the `strip_prefix` method - | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly + = note: `#[deny(clippy::approx_constant)]` on by default -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:228:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:18:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:227:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:28:19 | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs index f20841891a74..02892f329af6 100644 --- a/tests/ui/min_rust_version_invalid_attr.rs +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -2,3 +2,17 @@ #![clippy::msrv = "invalid.version"] fn main() {} + +#[clippy::msrv = "invalid.version"] +fn outer_attr() {} + +mod multiple { + #![clippy::msrv = "1.40"] + #![clippy::msrv = "=1.35.0"] + #![clippy::msrv = "1.10.1"] + + mod foo { + #![clippy::msrv = "1"] + #![clippy::msrv = "1.0.0"] + } +} diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr index 6ff88ca56f8b..93370a0fa9c9 100644 --- a/tests/ui/min_rust_version_invalid_attr.stderr +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -4,5 +4,47 @@ error: `invalid.version` is not a valid Rust version LL | #![clippy::msrv = "invalid.version"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: `msrv` cannot be an outer attribute + --> $DIR/min_rust_version_invalid_attr.rs:6:1 + | +LL | #[clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:11:5 + | +LL | #![clippy::msrv = "=1.35.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:12:5 + | +LL | #![clippy::msrv = "1.10.1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:16:9 + | +LL | #![clippy::msrv = "1.0.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:15:9 + | +LL | #![clippy::msrv = "1"] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/min_rust_version_multiple_inner_attr.rs b/tests/ui/min_rust_version_multiple_inner_attr.rs deleted file mode 100644 index e882d5ccf91a..000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.40"] -#![clippy::msrv = "=1.35.0"] -#![clippy::msrv = "1.10.1"] - -mod foo { - #![clippy::msrv = "1"] - #![clippy::msrv = "1.0.0"] -} - -fn main() {} diff --git a/tests/ui/min_rust_version_multiple_inner_attr.stderr b/tests/ui/min_rust_version_multiple_inner_attr.stderr deleted file mode 100644 index e3ff6605cde8..000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:3:1 - | -LL | #![clippy::msrv = "=1.35.0"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 - | -LL | #![clippy::msrv = "1.40"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:4:1 - | -LL | #![clippy::msrv = "1.10.1"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 - | -LL | #![clippy::msrv = "1.40"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:8:5 - | -LL | #![clippy::msrv = "1.0.0"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:7:5 - | -LL | #![clippy::msrv = "1"] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs deleted file mode 100644 index 98fffe1e3512..000000000000 --- a/tests/ui/min_rust_version_no_patch.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![allow(clippy::redundant_clone)] -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0"] - -fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -fn main() { - manual_strip_msrv() -} diff --git a/tests/ui/min_rust_version_outer_attr.rs b/tests/ui/min_rust_version_outer_attr.rs deleted file mode 100644 index 551948bd72ef..000000000000 --- a/tests/ui/min_rust_version_outer_attr.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![feature(custom_inner_attributes)] - -#[clippy::msrv = "invalid.version"] -fn main() {} diff --git a/tests/ui/min_rust_version_outer_attr.stderr b/tests/ui/min_rust_version_outer_attr.stderr deleted file mode 100644 index 579ee7a87d23..000000000000 --- a/tests/ui/min_rust_version_outer_attr.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: `msrv` cannot be an outer attribute - --> $DIR/min_rust_version_outer_attr.rs:3:1 - | -LL | #[clippy::msrv = "invalid.version"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 88f6935d224a..b85e88784918 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -77,5 +77,17 @@ mod const_fn_stabilized_before_msrv { } } +fn msrv_1_45() -> i32 { + #![clippy::msrv = "1.45"] + + 45 +} + +fn msrv_1_46() -> i32 { + #![clippy::msrv = "1.46"] + + 46 +} + // Should not be const fn main() {} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 3eb52b682747..f8e221c82f1a 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -81,5 +81,15 @@ LL | | byte.is_ascii_digit(); LL | | } | |_____^ -error: aborting due to 10 previous errors +error: this could be a `const fn` + --> $DIR/could_be_const.rs:86:1 + | +LL | / fn msrv_1_46() -> i32 { +LL | | #![clippy::msrv = "1.46"] +LL | | +LL | | 46 +LL | | } + | |_^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index 07d7f0b45b0c..bc376d0d7fb3 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -42,3 +43,17 @@ fn main() { // Issue #5927 let _ = opt.as_deref(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let opt = Some(String::from("123")); + let _ = opt.as_deref(); +} diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index 6ae059c9425d..ba3a2eedc225 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -45,3 +46,17 @@ fn main() { // Issue #5927 let _ = opt.as_ref().map(std::ops::Deref::deref); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index 62f282324752..7de8b3b6ba43 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -1,5 +1,5 @@ error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:13:13 + --> $DIR/option_as_ref_deref.rs:14:13 | LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` @@ -7,7 +7,7 @@ LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:16:13 + --> $DIR/option_as_ref_deref.rs:17:13 | LL | let _ = opt.clone() | _____________^ @@ -17,94 +17,100 @@ LL | | ) | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:22:13 + --> $DIR/option_as_ref_deref.rs:23:13 | LL | let _ = opt.as_mut().map(DerefMut::deref_mut); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:24:13 + --> $DIR/option_as_ref_deref.rs:25:13 | LL | let _ = opt.as_ref().map(String::as_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:25:13 + --> $DIR/option_as_ref_deref.rs:26:13 | LL | let _ = opt.as_ref().map(|x| x.as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:26:13 + --> $DIR/option_as_ref_deref.rs:27:13 | LL | let _ = opt.as_mut().map(String::as_mut_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:27:13 + --> $DIR/option_as_ref_deref.rs:28:13 | LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:28:13 + --> $DIR/option_as_ref_deref.rs:29:13 | LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:29:13 + --> $DIR/option_as_ref_deref.rs:30:13 | LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:30:13 + --> $DIR/option_as_ref_deref.rs:31:13 | LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:31:13 + --> $DIR/option_as_ref_deref.rs:32:13 | LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:32:13 + --> $DIR/option_as_ref_deref.rs:33:13 | LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:34:13 + --> $DIR/option_as_ref_deref.rs:35:13 | LL | let _ = opt.as_ref().map(|x| x.deref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:35:13 + --> $DIR/option_as_ref_deref.rs:36:13 | LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:42:13 + --> $DIR/option_as_ref_deref.rs:43:13 | LL | let _ = opt.as_ref().map(|x| &**x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:43:13 + --> $DIR/option_as_ref_deref.rs:44:13 | LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:46:13 + --> $DIR/option_as_ref_deref.rs:47:13 | LL | let _ = opt.as_ref().map(std::ops::Deref::deref); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` -error: aborting due to 17 previous errors +error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:61:13 + | +LL | let _ = opt.as_ref().map(String::as_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 85d021b2f25e..824f00cb99e8 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -1,10 +1,12 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_range_contains)] +#![allow(unused)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::unnecessary_operation)] + fn main() { let x = 9_i32; @@ -62,3 +64,17 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let x = 5; + x >= 8 && x < 34; +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let x = 5; + (8..35).contains(&x); +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 9a7a75dc1325..df925eeadfe5 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -1,10 +1,12 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_range_contains)] +#![allow(unused)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::unnecessary_operation)] + fn main() { let x = 9_i32; @@ -62,3 +64,17 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let x = 5; + x >= 8 && x < 34; +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let x = 5; + x >= 8 && x < 35; +} diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 936859db5a12..9689e665b05c 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -1,5 +1,5 @@ error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:12:5 + --> $DIR/range_contains.rs:14:5 | LL | x >= 8 && x < 12; | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` @@ -7,118 +7,124 @@ LL | x >= 8 && x < 12; = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:13:5 + --> $DIR/range_contains.rs:15:5 | LL | x < 42 && x >= 21; | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:14:5 + --> $DIR/range_contains.rs:16:5 | LL | 100 > x && 1 <= x; | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:17:5 + --> $DIR/range_contains.rs:19:5 | LL | x >= 9 && x <= 99; | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:18:5 + --> $DIR/range_contains.rs:20:5 | LL | x <= 33 && x >= 1; | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:19:5 + --> $DIR/range_contains.rs:21:5 | LL | 999 >= x && 1 <= x; | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:22:5 + --> $DIR/range_contains.rs:24:5 | LL | x < 8 || x >= 12; | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:23:5 + --> $DIR/range_contains.rs:25:5 | LL | x >= 42 || x < 21; | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:24:5 + --> $DIR/range_contains.rs:26:5 | LL | 100 <= x || 1 > x; | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:27:5 + --> $DIR/range_contains.rs:29:5 | LL | x < 9 || x > 99; | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:28:5 + --> $DIR/range_contains.rs:30:5 | LL | x > 33 || x < 1; | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:29:5 + --> $DIR/range_contains.rs:31:5 | LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:44:5 + --> $DIR/range_contains.rs:46:5 | LL | y >= 0. && y < 1.; | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:45:5 + --> $DIR/range_contains.rs:47:5 | LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:48:5 + --> $DIR/range_contains.rs:50:5 | LL | x >= -10 && x <= 10; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:50:5 + --> $DIR/range_contains.rs:52:5 | LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:30 + --> $DIR/range_contains.rs:57:30 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:5 + --> $DIR/range_contains.rs:57:5 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:29 + --> $DIR/range_contains.rs:58:29 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:5 + --> $DIR/range_contains.rs:58:5 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` -error: aborting due to 20 previous errors +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:79:5 + | +LL | x >= 8 && x < 35; + | ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)` + +error: aborting due to 21 previous errors diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index 5b4b8eeedd46..34ab552cb1d8 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,4 +1,6 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -69,3 +71,17 @@ fn issue_3476() { S { foo: foo:: }; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let start = 0; + let _ = RangeFrom { start: start }; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let start = 0; + let _ = RangeFrom { start }; +} diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 3f97b80c5682..a051b1f96f0f 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,4 +1,6 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -69,3 +71,17 @@ fn issue_3476() { S { foo: foo:: }; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let start = 0; + let _ = RangeFrom { start: start }; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let start = 0; + let _ = RangeFrom { start: start }; +} diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 7976292df224..8b82e062b93a 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -1,5 +1,5 @@ error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:34:9 + --> $DIR/redundant_field_names.rs:36:9 | LL | gender: gender, | ^^^^^^^^^^^^^^ help: replace it with: `gender` @@ -7,40 +7,46 @@ LL | gender: gender, = note: `-D clippy::redundant-field-names` implied by `-D warnings` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:35:9 + --> $DIR/redundant_field_names.rs:37:9 | LL | age: age, | ^^^^^^^^ help: replace it with: `age` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:56:25 + --> $DIR/redundant_field_names.rs:58:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:57:23 + --> $DIR/redundant_field_names.rs:59:23 | LL | let _ = RangeTo { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:21 + --> $DIR/redundant_field_names.rs:60:21 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:35 + --> $DIR/redundant_field_names.rs:60:35 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:60:32 + --> $DIR/redundant_field_names.rs:62:32 | LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` -error: aborting due to 7 previous errors +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:86:25 + | +LL | let _ = RangeFrom { start: start }; + | ^^^^^^^^^^^^ help: replace it with: `start` + +error: aborting due to 8 previous errors diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index acc8f1e25b6e..42110dbe81e8 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -54,3 +55,15 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + static V: &'static u8 = &16; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + static V: &u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index f2f0f78659c9..bc5200bc8625 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -54,3 +55,15 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + static V: &'static u8 = &16; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + static V: &'static u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 649831f9c069..735113460d28 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -1,5 +1,5 @@ error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:8:17 + --> $DIR/redundant_static_lifetimes.rs:9:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` @@ -7,94 +7,100 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:12:21 + --> $DIR/redundant_static_lifetimes.rs:13:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:14:32 + --> $DIR/redundant_static_lifetimes.rs:15:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:14:47 + --> $DIR/redundant_static_lifetimes.rs:15:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:16:17 + --> $DIR/redundant_static_lifetimes.rs:17:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:18:20 + --> $DIR/redundant_static_lifetimes.rs:19:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:20:19 + --> $DIR/redundant_static_lifetimes.rs:21:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:22:19 + --> $DIR/redundant_static_lifetimes.rs:23:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:24:19 + --> $DIR/redundant_static_lifetimes.rs:25:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:26:25 + --> $DIR/redundant_static_lifetimes.rs:27:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:30:29 + --> $DIR/redundant_static_lifetimes.rs:31:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:32:25 + --> $DIR/redundant_static_lifetimes.rs:33:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:34:28 + --> $DIR/redundant_static_lifetimes.rs:35:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:36:27 + --> $DIR/redundant_static_lifetimes.rs:37:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:38:27 + --> $DIR/redundant_static_lifetimes.rs:39:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:40:27 + --> $DIR/redundant_static_lifetimes.rs:41:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` -error: aborting due to 16 previous errors +error: statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:68:16 + | +LL | static V: &'static u8 = &17; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: aborting due to 17 previous errors diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index c223b5bc711b..9786c7b12128 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -1,9 +1,9 @@ // run-rustfix -#![feature(box_patterns)] +#![feature(box_patterns, custom_inner_attributes)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,15 @@ fn main() { if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + if let [1] | [52] = [0] {} +} + +fn msrv_1_53() { + #![clippy::msrv = "1.53"] + + if let [1 | 53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 04cd11036e4e..f57322396d4a 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -1,9 +1,9 @@ // run-rustfix -#![feature(box_patterns)] +#![feature(box_patterns, custom_inner_attributes)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,15 @@ fn main() { if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + if let [1] | [52] = [0] {} +} + +fn msrv_1_53() { + #![clippy::msrv = "1.53"] + + if let [1] | [53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index 453c66cbba8f..fbc12fff0b0e 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -175,5 +175,16 @@ help: nest the patterns LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | ~~~~~~~~~~~~~~~~~ -error: aborting due to 16 previous errors +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:46:12 + | +LL | if let [1] | [53] = [0] {} + | ^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let [1 | 53] = [0] {} + | ~~~~~~~~ + +error: aborting due to 17 previous errors diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 37986187da17..3b54fe9d5ff3 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -617,3 +618,35 @@ mod issue6902 { Bar = 1, } } + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + Self::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 1b2b3337c92e..bf87633cd2d8 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -617,3 +618,35 @@ mod issue6902 { Bar = 1, } } + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index f06bb959b3bd..16fb0609242c 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:22:21 + --> $DIR/use_self.rs:23:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -7,244 +7,250 @@ LL | fn new() -> Foo { = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:13 + --> $DIR/use_self.rs:24:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:25:22 + --> $DIR/use_self.rs:26:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:26:13 + --> $DIR/use_self.rs:27:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:31:25 + --> $DIR/use_self.rs:32:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:32:13 + --> $DIR/use_self.rs:33:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:97:24 + --> $DIR/use_self.rs:98:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:97:55 + --> $DIR/use_self.rs:98:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:112:13 + --> $DIR/use_self.rs:113:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:147:29 + --> $DIR/use_self.rs:148:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:21 + --> $DIR/use_self.rs:149:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:159:21 + --> $DIR/use_self.rs:160:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:160:13 + --> $DIR/use_self.rs:161:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:177:21 + --> $DIR/use_self.rs:178:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:178:21 + --> $DIR/use_self.rs:179:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:179:21 + --> $DIR/use_self.rs:180:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:221:13 + --> $DIR/use_self.rs:222:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:222:13 + --> $DIR/use_self.rs:223:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:224:13 + --> $DIR/use_self.rs:225:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:243:13 + --> $DIR/use_self.rs:244:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:257:25 + --> $DIR/use_self.rs:258:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:13 + --> $DIR/use_self.rs:259:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:262:16 + --> $DIR/use_self.rs:263:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:262:22 + --> $DIR/use_self.rs:263:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:285:29 + --> $DIR/use_self.rs:286:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:286:13 + --> $DIR/use_self.rs:287:13 | LL | Foo:: { value } | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:458:13 + --> $DIR/use_self.rs:459:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:495:13 + --> $DIR/use_self.rs:496:13 | LL | S2::new() | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:532:17 + --> $DIR/use_self.rs:533:17 | LL | Foo::Bar => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:533:17 + --> $DIR/use_self.rs:534:17 | LL | Foo::Baz => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:539:20 + --> $DIR/use_self.rs:540:20 | LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:563:17 + --> $DIR/use_self.rs:564:17 | LL | Something::Num(n) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:564:17 + --> $DIR/use_self.rs:565:17 | LL | Something::TupleNums(n, _m) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:565:17 + --> $DIR/use_self.rs:566:17 | LL | Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:571:17 + --> $DIR/use_self.rs:572:17 | LL | crate::issue8845::Something::Num(n) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:572:17 + --> $DIR/use_self.rs:573:17 | LL | crate::issue8845::Something::TupleNums(n, _m) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:573:17 + --> $DIR/use_self.rs:574:17 | LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:589:17 + --> $DIR/use_self.rs:590:17 | LL | let Foo(x) = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:594:17 + --> $DIR/use_self.rs:595:17 | LL | let crate::issue8845::Foo(x) = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:601:17 + --> $DIR/use_self.rs:602:17 | LL | let Bar { x, .. } = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:606:17 + --> $DIR/use_self.rs:607:17 | LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 41 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:648:17 + | +LL | E::A => {}, + | ^ help: use the applicable keyword: `Self` + +error: aborting due to 42 previous errors From 2ed404937f2e9fcd481c717ef4d386d053ea59e3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 16 Oct 2022 18:48:28 +0000 Subject: [PATCH 055/524] Introduce subst_iter and subst_iter_copied on EarlyBinder --- clippy_utils/src/ty.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a15daec7c3ce..3b5a9ba83568 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -657,21 +657,18 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O let mut output = None; let lang_items = cx.tcx.lang_items(); - for pred in cx + for (pred, _) in cx .tcx .bound_explicit_item_bounds(ty.item_def_id) - .transpose_iter() - .map(|x| x.map_bound(|(p, _)| p)) + .subst_iter_copied(cx.tcx, ty.substs) { - match pred.0.kind().skip_binder() { + match pred.kind().skip_binder() { PredicateKind::Trait(p) if (lang_items.fn_trait() == Some(p.def_id()) || lang_items.fn_mut_trait() == Some(p.def_id()) || lang_items.fn_once_trait() == Some(p.def_id())) => { - let i = pred - .map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1))) - .subst(cx.tcx, ty.substs); + let i = pred.kind().rebind(p.trait_ref.substs.type_at(1)); if inputs.map_or(false, |inputs| inputs != i) { // Multiple different fn trait impls. Is this even allowed? @@ -684,10 +681,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O // Multiple different fn trait impls. Is this even allowed? return None; } - output = Some( - pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap())) - .subst(cx.tcx, ty.substs), - ); + output = pred.kind().rebind(p.term.ty()).transpose(); }, _ => (), } From 5c6c3534d9648472940efe00d3d8fb9e15bdd1a2 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 22 Oct 2022 07:37:10 -0400 Subject: [PATCH 056/524] Add `lintcheck` to packages linted by `dogfood` test --- tests/dogfood.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 961525bbd910..6d0022f7a5cc 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -20,7 +20,14 @@ fn dogfood_clippy() { } // "" is the root package - for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { + for package in &[ + "", + "clippy_dev", + "clippy_lints", + "clippy_utils", + "lintcheck", + "rustc_tools_util", + ] { run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); } } From e38bb1a963749ac4e94c13751233bdc61f20e84f Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 22 Oct 2022 07:37:23 -0400 Subject: [PATCH 057/524] Apply `--fix` fixes --- lintcheck/src/config.rs | 2 +- lintcheck/src/main.rs | 53 +++++++++++++++++++---------------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index b344db634f61..39ddf0d4d5ab 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -97,7 +97,7 @@ impl LintcheckConfig { Some(&0) => { // automatic choice // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize + rayon::current_num_threads() / 2 }, Some(&threads) => threads, // no -j passed, use a single thread diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 95b20d7f0242..b95e889a50fa 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -144,12 +144,12 @@ impl ClippyWarning { } let mut output = String::from("| "); - let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); + let _ = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line); let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); output.push('\n'); output } else { - format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message) + format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.message) } } } @@ -161,10 +161,10 @@ fn get(path: &str) -> Result { match ureq::get(path).call() { Ok(res) => return Ok(res), Err(e) if retries >= MAX_RETRIES => return Err(e), - Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e), + Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"), Err(e) => return Err(e), } - eprintln!("retrying in {} seconds...", retries); + eprintln!("retrying in {retries} seconds..."); thread::sleep(Duration::from_secs(retries as u64)); retries += 1; } @@ -181,11 +181,11 @@ impl CrateSource { let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); // url to download the crate from crates.io - let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version); - println!("Downloading and extracting {} {} from {}", name, version, url); + let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download"); + println!("Downloading and extracting {name} {version} from {url}"); create_dirs(&krate_download_dir, &extract_dir); - let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version)); + let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz")); // don't download/extract if we already have done so if !krate_file_path.is_file() { // create a file path to download and write the crate data into @@ -205,7 +205,7 @@ impl CrateSource { Crate { version: version.clone(), name: name.clone(), - path: extract_dir.join(format!("{}-{}/", name, version)), + path: extract_dir.join(format!("{name}-{version}/")), options: options.clone(), } }, @@ -218,12 +218,12 @@ impl CrateSource { let repo_path = { let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); // add a -git suffix in case we have the same crate from crates.io and a git repo - repo_path.push(format!("{}-git", name)); + repo_path.push(format!("{name}-git")); repo_path }; // clone the repo if we have not done so if !repo_path.is_dir() { - println!("Cloning {} and checking out {}", url, commit); + println!("Cloning {url} and checking out {commit}"); if !Command::new("git") .arg("clone") .arg(url) @@ -232,7 +232,7 @@ impl CrateSource { .expect("Failed to clone git repo!") .success() { - eprintln!("Failed to clone {} into {}", url, repo_path.display()) + eprintln!("Failed to clone {url} into {}", repo_path.display()) } } // check out the commit/branch/whatever @@ -245,7 +245,7 @@ impl CrateSource { .expect("Failed to check out commit") .success() { - eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display()) + eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()) } Crate { @@ -261,11 +261,11 @@ impl CrateSource { // as a result of this filter. let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); if dest_crate_root.exists() { - println!("Deleting existing directory at {:?}", dest_crate_root); + println!("Deleting existing directory at {dest_crate_root:?}"); std::fs::remove_dir_all(&dest_crate_root).unwrap(); } - println!("Copying {:?} to {:?}", path, dest_crate_root); + println!("Copying {path:?} to {dest_crate_root:?}"); fn is_cache_dir(entry: &DirEntry) -> bool { std::fs::read(entry.path().join("CACHEDIR.TAG")) @@ -389,10 +389,7 @@ impl Crate { let all_output = Command::new(&cargo_clippy_path) // use the looping index to create individual target dirs - .env( - "CARGO_TARGET_DIR", - shared_target_dir.join(format!("_{:?}", thread_index)), - ) + .env("CARGO_TARGET_DIR", shared_target_dir.join(format!("_{thread_index:?}"))) .args(&cargo_clippy_args) .current_dir(&self.path) .output() @@ -422,8 +419,8 @@ impl Crate { { let subcrate = &stderr[63..]; println!( - "ERROR: failed to apply some suggetion to {} / to (sub)crate {}", - self.name, subcrate + "ERROR: failed to apply some suggetion to {} / to (sub)crate {subcrate}", + self.name ); } // fast path, we don't need the warnings anyway @@ -459,7 +456,7 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = std::fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = - toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e)); + toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display())); // parse the hashmap of the toml file into a list of crates let tomlcrates: Vec = crate_list.crates.into_values().collect(); @@ -498,7 +495,7 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) || tk.git_hash.is_some() != tk.git_url.is_some() { - eprintln!("tomlkrate: {:?}", tk); + eprintln!("tomlkrate: {tk:?}"); if tk.git_hash.is_some() != tk.git_url.is_some() { panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!"); } @@ -526,13 +523,13 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect(); // sort by "000{count} {clippy::lintname}" // to not have a lint with 200 and 2 warnings take the same spot - stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint)); + stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}")); let mut header = String::from("| lint | count |\n"); header.push_str("| -------------------------------------------------- | ----- |\n"); let stats_string = stats .iter() - .map(|(lint, count)| format!("| {:<50} | {:>4} |\n", lint, count)) + .map(|(lint, count)| format!("| {lint:<50} | {count:>4} |\n")) .fold(header, |mut table, line| { table.push_str(&line); table @@ -731,7 +728,7 @@ fn main() { write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); for (cratename, msg) in ices.iter() { - let _ = write!(text, "{}: '{}'", cratename, msg); + let _ = write!(text, "{cratename}: '{msg}'"); } println!("Writing logs to {}", config.lintcheck_results_path.display()); @@ -795,7 +792,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us .iter() .filter(|(new_key, _)| old_stats_deduped.get::(new_key).is_none()) .for_each(|(new_key, new_value)| { - println!("{} 0 => {}", new_key, new_value); + println!("{new_key} 0 => {new_value}"); }); // list all changed counts (key is in both maps but value differs) @@ -804,7 +801,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us .filter(|(new_key, _new_val)| old_stats_deduped.get::(new_key).is_some()) .for_each(|(new_key, new_val)| { let old_val = old_stats_deduped.get::(new_key).unwrap(); - println!("{} {} => {}", new_key, old_val, new_val); + println!("{new_key} {old_val} => {new_val}"); }); // list all gone counts (key is in old status but not in new stats) @@ -813,7 +810,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us .filter(|(old_key, _)| new_stats_deduped.get::<&String>(old_key).is_none()) .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key)) .for_each(|(old_key, old_value)| { - println!("{} {} => 0", old_key, old_value); + println!("{old_key} {old_value} => 0"); }); } From bbee1c9d1f53fc8614fa2dc2737329555bc01b26 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sat, 22 Oct 2022 07:12:07 -0400 Subject: [PATCH 058/524] Apply manual fixes --- lintcheck/src/config.rs | 3 +- lintcheck/src/driver.rs | 2 +- lintcheck/src/main.rs | 85 +++++++++++++++++++++----------------- lintcheck/src/recursive.rs | 8 ++-- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index 39ddf0d4d5ab..b8824024e6c7 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -73,8 +73,7 @@ impl LintcheckConfig { let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { clap_config .get_one::("crates-toml") - .map(|s| &**s) - .unwrap_or("lintcheck/lintcheck_crates.toml") + .map_or("lintcheck/lintcheck_crates.toml", |s| &**s) .into() }); diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs index 63221bab32d3..47724a2fedb0 100644 --- a/lintcheck/src/driver.rs +++ b/lintcheck/src/driver.rs @@ -5,7 +5,7 @@ use std::net::TcpStream; use std::process::{self, Command, Stdio}; use std::{env, mem}; -/// 1. Sends [DriverInfo] to the [crate::recursive::LintcheckServer] running on `addr` +/// 1. Sends [`DriverInfo`] to the [`crate::recursive::LintcheckServer`] running on `addr` /// 2. Receives [bool] from the server, if `false` returns `None` /// 3. Otherwise sends the stderr of running `clippy-driver` to the server fn run_clippy(addr: &str) -> Option { diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index b95e889a50fa..54c1b80c42db 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -116,12 +116,13 @@ impl ClippyWarning { let span = diag.spans.into_iter().find(|span| span.is_primary)?; - let file = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { - Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), - Err(_) => format!( + let file = if let Ok(stripped) = Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { + format!("$CARGO_HOME/{}", stripped.display()) + } else { + format!( "target/lintcheck/sources/{}-{}/{}", crate_name, crate_version, span.file_name - ), + ) }; Some(Self { @@ -154,6 +155,7 @@ impl ClippyWarning { } } +#[allow(clippy::result_large_err)] fn get(path: &str) -> Result { const MAX_RETRIES: u8 = 4; let mut retries = 0; @@ -165,7 +167,7 @@ fn get(path: &str) -> Result { Err(e) => return Err(e), } eprintln!("retrying in {retries} seconds..."); - thread::sleep(Duration::from_secs(retries as u64)); + thread::sleep(Duration::from_secs(u64::from(retries))); retries += 1; } } @@ -232,7 +234,7 @@ impl CrateSource { .expect("Failed to clone git repo!") .success() { - eprintln!("Failed to clone {url} into {}", repo_path.display()) + eprintln!("Failed to clone {url} into {}", repo_path.display()); } } // check out the commit/branch/whatever @@ -245,7 +247,7 @@ impl CrateSource { .expect("Failed to check out commit") .success() { - eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()) + eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()); } Crate { @@ -256,6 +258,12 @@ impl CrateSource { } }, CrateSource::Path { name, path, options } => { + fn is_cache_dir(entry: &DirEntry) -> bool { + std::fs::read(entry.path().join("CACHEDIR.TAG")) + .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) + .unwrap_or(false) + } + // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file. // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory // as a result of this filter. @@ -267,12 +275,6 @@ impl CrateSource { println!("Copying {path:?} to {dest_crate_root:?}"); - fn is_cache_dir(entry: &DirEntry) -> bool { - std::fs::read(entry.path().join("CACHEDIR.TAG")) - .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) - .unwrap_or(false) - } - for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) { let entry = entry.unwrap(); let entry_path = entry.path(); @@ -301,6 +303,7 @@ impl CrateSource { impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued + #[allow(clippy::too_many_arguments)] fn run_clippy_lints( &self, cargo_clippy_path: &Path, @@ -345,14 +348,14 @@ impl Crate { clippy_args.push(opt); } } else { - clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]) + clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]); } if lint_filter.is_empty() { clippy_args.push("--cap-lints=warn"); } else { clippy_args.push("--cap-lints=allow"); - clippy_args.extend(lint_filter.iter().map(|filter| filter.as_str())) + clippy_args.extend(lint_filter.iter().map(std::string::String::as_str)); } if let Some(server) = server { @@ -463,7 +466,7 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => // multiple Cratesources) let mut crate_sources = Vec::new(); - tomlcrates.into_iter().for_each(|tk| { + for tk in tomlcrates { if let Some(ref path) = tk.path { crate_sources.push(CrateSource::Path { name: tk.name.clone(), @@ -472,13 +475,13 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { }); } else if let Some(ref versions) = tk.versions { // if we have multiple versions, save each one - versions.iter().for_each(|ver| { + for ver in versions.iter() { crate_sources.push(CrateSource::CratesIo { name: tk.name.clone(), version: ver.to_string(), options: tk.options.clone(), }); - }) + } } else if tk.git_url.is_some() && tk.git_hash.is_some() { // otherwise, we should have a git source crate_sources.push(CrateSource::Git { @@ -496,15 +499,18 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { || tk.git_hash.is_some() != tk.git_url.is_some() { eprintln!("tomlkrate: {tk:?}"); - if tk.git_hash.is_some() != tk.git_url.is_some() { - panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!"); - } - if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) { - panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"); - } + assert_eq!( + tk.git_hash.is_some(), + tk.git_url.is_some(), + "Error: Encountered TomlCrate with only one of git_hash and git_url!" + ); + assert!( + tk.path.is_none() || (tk.git_hash.is_none() && tk.versions.is_none()), + "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields" + ); unreachable!("Failed to translate TomlCrate into CrateSource!"); } - }); + } // sort the crates crate_sources.sort(); @@ -566,6 +572,7 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool logs_modified < clippy_modified } +#[allow(clippy::too_many_lines)] fn main() { // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` if let Ok(addr) = env::var("LINTCHECK_SERVER") { @@ -671,7 +678,7 @@ fn main() { .unwrap(); let server = config.recursive.then(|| { - let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); + fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive").unwrap_or_default(); LintcheckServer::spawn(recursive_options) }); @@ -727,7 +734,7 @@ fn main() { } write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); - for (cratename, msg) in ices.iter() { + for (cratename, msg) in &ices { let _ = write!(text, "{cratename}: '{msg}'"); } @@ -780,10 +787,10 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us let mut new_stats_deduped = new_stats; // remove duplicates from both hashmaps - same_in_both_hashmaps.iter().for_each(|(k, v)| { + for (k, v) in &same_in_both_hashmaps { assert!(old_stats_deduped.remove(k) == Some(*v)); assert!(new_stats_deduped.remove(k) == Some(*v)); - }); + } println!("\nStats:"); @@ -821,19 +828,21 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us /// This function panics if creating one of the dirs fails. fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create lintcheck target dir"); - } + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create lintcheck target dir" + ); }); std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate download dir"); - } + assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir"); }); std::fs::create_dir(extract_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate extraction dir"); - } + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create crate extraction dir" + ); }); } diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs index 67dcfc2b199c..49072e65192f 100644 --- a/lintcheck/src/recursive.rs +++ b/lintcheck/src/recursive.rs @@ -1,7 +1,7 @@ //! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`, -//! this allows [crate::driver] to be run for every dependency. The driver connects to -//! [LintcheckServer] to ask if it should be skipped, and if not sends the stderr of running clippy -//! on the crate to the server +//! this allows [`crate::driver`] to be run for every dependency. The driver connects to +//! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running +//! clippy on the crate to the server use crate::ClippyWarning; use crate::RecursiveOptions; @@ -109,8 +109,8 @@ impl LintcheckServer { Self { local_addr, - sender, receiver, + sender, } } From a0c82d2bcce1b636c02bb6bf942c05af8dc3ac9a Mon Sep 17 00:00:00 2001 From: kraktus <56031107+kraktus@users.noreply.github.com> Date: Sat, 22 Oct 2022 21:07:05 +0200 Subject: [PATCH 059/524] Explicitly mention [cfg(test)] Co-authored-by: llogiq --- clippy_lints/src/utils/conf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 199dfafebf35..a10de31556c8 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -373,7 +373,7 @@ define_Conf! { (max_include_file_size: u64 = 1_000_000), /// Lint: EXPECT_USED. /// - /// Whether `expect` should be allowed in test cfg + /// Whether `expect` should be allowed within `#[cfg(test)]` (allow_expect_in_tests: bool = false), /// Lint: UNWRAP_USED. /// From cd0bb7de01d6f516c2e69c03f3bed26aaf167bc8 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 23 Oct 2022 15:18:45 +0200 Subject: [PATCH 060/524] Merge commit '4f142aa1058f14f153f8bfd2d82f04ddb9982388' into clippyup --- .github/workflows/clippy.yml | 1 + .github/workflows/clippy_bors.yml | 1 + .github/workflows/clippy_dev.yml | 2 + CHANGELOG.md | 6 + CONTRIBUTING.md | 2 +- book/src/development/adding_lints.md | 23 +- book/src/development/basics.md | 7 +- clippy_dev/src/serve.rs | 2 +- clippy_dev/src/setup/intellij.rs | 17 +- clippy_dev/src/update_lints.rs | 10 +- clippy_lints/src/booleans.rs | 6 +- clippy_lints/src/box_default.rs | 96 +- clippy_lints/src/casts/as_ptr_cast_mut.rs | 38 + clippy_lints/src/casts/cast_nan_to_int.rs | 28 + clippy_lints/src/casts/mod.rs | 57 +- clippy_lints/src/casts/unnecessary_cast.rs | 3 - clippy_lints/src/comparison_chain.rs | 8 +- clippy_lints/src/default_numeric_fallback.rs | 65 +- clippy_lints/src/dereference.rs | 99 +- clippy_lints/src/derive.rs | 5 +- clippy_lints/src/disallowed_methods.rs | 5 +- clippy_lints/src/entry.rs | 20 +- clippy_lints/src/eta_reduction.rs | 5 +- clippy_lints/src/format_args.rs | 165 +- clippy_lints/src/from_over_into.rs | 176 +- clippy_lints/src/functions/must_use.rs | 10 +- clippy_lints/src/functions/too_many_lines.rs | 5 +- clippy_lints/src/implicit_saturating_sub.rs | 2 +- .../src/invalid_upcast_comparisons.rs | 4 +- clippy_lints/src/large_enum_variant.rs | 5 +- clippy_lints/src/let_underscore.rs | 23 +- clippy_lints/src/lib.register_all.rs | 5 + clippy_lints/src/lib.register_complexity.rs | 2 + clippy_lints/src/lib.register_internal.rs | 32 +- clippy_lints/src/lib.register_lints.rs | 38 +- clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.register_pedantic.rs | 2 - clippy_lints/src/lib.register_restriction.rs | 2 + clippy_lints/src/lib.register_style.rs | 2 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 43 +- clippy_lints/src/loops/needless_range_loop.rs | 35 +- .../src/loops/while_let_on_iterator.rs | 5 +- clippy_lints/src/manual_async_fn.rs | 18 +- clippy_lints/src/manual_clamp.rs | 10 +- clippy_lints/src/matches/collapsible_match.rs | 23 +- clippy_lints/src/matches/manual_filter.rs | 153 ++ clippy_lints/src/matches/manual_map.rs | 318 +--- clippy_lints/src/matches/manual_utils.rs | 277 +++ clippy_lints/src/matches/match_same_arms.rs | 6 +- .../src/matches/match_single_binding.rs | 31 +- clippy_lints/src/matches/mod.rs | 40 + clippy_lints/src/matches/single_match.rs | 11 +- clippy_lints/src/methods/into_iter_on_ref.rs | 5 +- .../methods/manual_saturating_arithmetic.rs | 10 +- clippy_lints/src/methods/mod.rs | 25 +- .../src/methods/option_as_ref_deref.rs | 20 +- clippy_lints/src/methods/or_fun_call.rs | 23 +- clippy_lints/src/methods/str_splitn.rs | 4 +- .../src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/misc_early/literal_suffix.rs | 4 +- .../src/misc_early/mixed_case_hex_literals.rs | 4 +- .../src/misc_early/zero_prefixed_literal.rs | 18 +- .../src/mismatching_type_param_order.rs | 5 +- clippy_lints/src/missing_trait_methods.rs | 98 + .../src/mixed_read_write_in_expression.rs | 5 +- clippy_lints/src/needless_for_each.rs | 5 +- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 8 +- clippy_lints/src/non_copy_const.rs | 5 +- .../src/non_octal_unix_permissions.rs | 17 +- clippy_lints/src/nonstandard_macro_braces.rs | 2 +- clippy_lints/src/operators/cmp_owned.rs | 18 +- clippy_lints/src/partial_pub_fields.rs | 81 + clippy_lints/src/ptr.rs | 54 +- clippy_lints/src/ptr_offset_with_cast.rs | 10 +- clippy_lints/src/redundant_clone.rs | 454 +---- clippy_lints/src/ref_option_ref.rs | 3 +- clippy_lints/src/shadow.rs | 5 +- clippy_lints/src/transmute/utils.rs | 24 +- clippy_lints/src/types/rc_buffer.rs | 23 +- .../src/types/redundant_allocation.rs | 8 +- clippy_lints/src/unit_return_expecting_ord.rs | 17 +- clippy_lints/src/unnested_or_patterns.rs | 5 +- clippy_lints/src/unused_io_amount.rs | 5 +- clippy_lints/src/useless_conversion.rs | 5 +- clippy_lints/src/utils/author.rs | 117 +- clippy_lints/src/utils/internal_lints.rs | 1576 +---------------- .../internal_lints/clippy_lints_internal.rs | 49 + .../utils/internal_lints/collapsible_calls.rs | 245 +++ .../internal_lints/compiler_lint_functions.rs | 77 + .../utils/internal_lints/if_chain_style.rs | 164 ++ .../interning_defined_symbol.rs | 239 +++ .../src/utils/internal_lints/invalid_paths.rs | 108 ++ .../internal_lints/lint_without_lint_pass.rs | 342 ++++ .../internal_lints/metadata_collector.rs | 8 +- .../utils/internal_lints/msrv_attr_impl.rs | 63 + .../internal_lints/outer_expn_data_pass.rs | 62 + .../src/utils/internal_lints/produce_ice.rs | 37 + .../internal_lints/unnecessary_def_path.rs | 343 ++++ clippy_utils/src/attrs.rs | 2 +- clippy_utils/src/consts.rs | 44 +- clippy_utils/src/eager_or_lazy.rs | 2 +- clippy_utils/src/lib.rs | 48 +- clippy_utils/src/macros.rs | 43 +- clippy_utils/src/mir/maybe_storage_live.rs | 52 + clippy_utils/src/mir/mod.rs | 164 ++ clippy_utils/src/mir/possible_borrower.rs | 241 +++ clippy_utils/src/mir/possible_origin.rs | 59 + clippy_utils/src/mir/transitive_relation.rs | 29 + clippy_utils/src/numeric_literal.rs | 7 +- clippy_utils/src/paths.rs | 36 - clippy_utils/src/sugg.rs | 13 +- lintcheck/src/config.rs | 5 +- lintcheck/src/driver.rs | 2 +- lintcheck/src/main.rs | 162 +- lintcheck/src/recursive.rs | 8 +- rust-toolchain | 2 +- src/docs.rs | 6 + src/docs/as_ptr_cast_mut.txt | 19 + src/docs/box_default.txt | 6 - src/docs/cast_nan_to_int.txt | 15 + src/docs/manual_filter.txt | 21 + src/docs/missing_trait_methods.txt | 40 + src/docs/partial_pub_fields.txt | 27 + src/docs/unused_format_specs.txt | 24 + tests/compile-test.rs | 2 +- tests/dogfood.rs | 9 +- tests/ui-internal/custom_ice_message.stderr | 2 +- tests/ui-internal/invalid_paths.rs | 2 +- tests/ui-internal/unnecessary_def_path.fixed | 6 +- tests/ui-internal/unnecessary_def_path.rs | 6 +- .../unnecessary_def_path_hardcoded_path.rs | 16 + ...unnecessary_def_path_hardcoded_path.stderr | 27 + tests/ui/as_ptr_cast_mut.rs | 37 + tests/ui/as_ptr_cast_mut.stderr | 16 + tests/ui/author.stdout | 24 +- tests/ui/author/blocks.stdout | 116 +- tests/ui/author/call.stdout | 28 +- tests/ui/author/if.stdout | 92 +- tests/ui/author/issue_3849.stdout | 24 +- tests/ui/author/loop.stdout | 202 +-- tests/ui/author/matches.stdout | 72 +- tests/ui/author/repeat.stdout | 20 +- tests/ui/author/struct.stdout | 112 +- tests/ui/box_default.fixed | 57 + tests/ui/box_default.rs | 28 +- tests/ui/box_default.stderr | 83 +- tests/ui/box_default_no_std.rs | 33 + tests/ui/cast_abs_to_unsigned.fixed | 18 +- tests/ui/cast_abs_to_unsigned.rs | 18 +- tests/ui/cast_abs_to_unsigned.stderr | 42 +- tests/ui/cast_lossless_bool.fixed | 13 + tests/ui/cast_lossless_bool.rs | 13 + tests/ui/cast_lossless_bool.stderr | 34 +- tests/ui/cast_nan_to_int.rs | 18 + tests/ui/cast_nan_to_int.stderr | 51 + tests/ui/cfg_attr_rustfmt.fixed | 16 +- tests/ui/cfg_attr_rustfmt.rs | 16 +- tests/ui/cfg_attr_rustfmt.stderr | 8 +- tests/ui/checked_conversions.fixed | 16 + tests/ui/checked_conversions.rs | 16 + tests/ui/checked_conversions.stderr | 40 +- tests/ui/cloned_instead_of_copied.fixed | 24 + tests/ui/cloned_instead_of_copied.rs | 24 + tests/ui/cloned_instead_of_copied.stderr | 30 +- tests/ui/collapsible_match.rs | 21 + tests/ui/collapsible_match.stderr | 34 +- tests/ui/crashes/ice-9625.rs | 4 + tests/ui/default_numeric_fallback_f64.fixed | 9 + tests/ui/default_numeric_fallback_f64.rs | 9 + tests/ui/default_numeric_fallback_f64.stderr | 34 +- tests/ui/default_numeric_fallback_i32.fixed | 10 + tests/ui/default_numeric_fallback_i32.rs | 10 + tests/ui/default_numeric_fallback_i32.stderr | 34 +- tests/ui/err_expect.fixed | 17 + tests/ui/err_expect.rs | 17 + tests/ui/err_expect.stderr | 10 +- tests/ui/filter_map_next_fixable.fixed | 16 + tests/ui/filter_map_next_fixable.rs | 16 + tests/ui/filter_map_next_fixable.stderr | 10 +- tests/ui/format_args.fixed | 3 + tests/ui/format_args.rs | 3 + tests/ui/format_args.stderr | 60 +- tests/ui/from_over_into.fixed | 87 + tests/ui/from_over_into.rs | 66 + tests/ui/from_over_into.stderr | 70 +- tests/ui/from_over_into_unfixable.rs | 35 + tests/ui/from_over_into_unfixable.stderr | 29 + tests/ui/implicit_saturating_sub.fixed | 50 + tests/ui/implicit_saturating_sub.rs | 50 + tests/ui/implicit_saturating_sub.stderr | 46 +- tests/ui/literals.rs | 7 + tests/ui/literals.stderr | 35 +- tests/ui/manual_assert.edition2018.fixed | 39 +- tests/ui/manual_assert.edition2018.stderr | 67 +- tests/ui/manual_assert.edition2021.fixed | 4 +- tests/ui/manual_assert.rs | 4 +- tests/ui/manual_clamp.rs | 27 + tests/ui/manual_clamp.stderr | 85 +- tests/ui/manual_filter.fixed | 119 ++ tests/ui/manual_filter.rs | 243 +++ tests/ui/manual_filter.stderr | 191 ++ tests/ui/manual_rem_euclid.fixed | 30 + tests/ui/manual_rem_euclid.rs | 30 + tests/ui/manual_rem_euclid.stderr | 30 +- tests/ui/manual_strip.rs | 19 + tests/ui/manual_strip.stderr | 47 +- tests/ui/map_unwrap_or.rs | 20 +- tests/ui/map_unwrap_or.stderr | 30 +- tests/ui/match_expr_like_matches_macro.fixed | 16 + tests/ui/match_expr_like_matches_macro.rs | 19 + tests/ui/match_expr_like_matches_macro.stderr | 38 +- tests/ui/match_overlapping_arm.rs | 1 - tests/ui/match_overlapping_arm.stderr | 32 +- tests/ui/match_single_binding.fixed | 9 + tests/ui/match_single_binding.rs | 8 + tests/ui/match_single_binding.stderr | 19 +- .../ui/match_wild_err_arm.edition2021.stderr | 35 - tests/ui/match_wild_err_arm.rs | 3 - ...n2018.stderr => match_wild_err_arm.stderr} | 8 +- tests/ui/mem_replace.fixed | 18 +- tests/ui/mem_replace.rs | 18 +- tests/ui/mem_replace.stderr | 46 +- tests/ui/min_rust_version_attr.rs | 239 +-- tests/ui/min_rust_version_attr.stderr | 46 +- tests/ui/min_rust_version_invalid_attr.rs | 14 + tests/ui/min_rust_version_invalid_attr.stderr | 44 +- .../min_rust_version_multiple_inner_attr.rs | 11 - ...in_rust_version_multiple_inner_attr.stderr | 38 - tests/ui/min_rust_version_no_patch.rs | 14 - tests/ui/min_rust_version_outer_attr.rs | 4 - tests/ui/min_rust_version_outer_attr.stderr | 8 - .../ui/missing_const_for_fn/could_be_const.rs | 12 + .../could_be_const.stderr | 12 +- tests/ui/missing_trait_methods.rs | 50 + tests/ui/missing_trait_methods.stderr | 27 + tests/ui/needless_borrow.fixed | 62 +- tests/ui/needless_borrow.rs | 60 +- tests/ui/needless_borrow.stderr | 96 +- tests/ui/option_as_ref_deref.fixed | 17 +- tests/ui/option_as_ref_deref.rs | 17 +- tests/ui/option_as_ref_deref.stderr | 42 +- tests/ui/or_fun_call.fixed | 11 + tests/ui/or_fun_call.rs | 11 + tests/ui/partial_pub_fields.rs | 40 + tests/ui/partial_pub_fields.stderr | 35 + tests/ui/ptr_arg.rs | 30 +- tests/ui/ptr_arg.stderr | 20 +- tests/ui/range_contains.fixed | 26 +- tests/ui/range_contains.rs | 26 +- tests/ui/range_contains.stderr | 48 +- tests/ui/redundant_field_names.fixed | 16 + tests/ui/redundant_field_names.rs | 16 + tests/ui/redundant_field_names.stderr | 22 +- tests/ui/redundant_static_lifetimes.fixed | 13 + tests/ui/redundant_static_lifetimes.rs | 13 + tests/ui/redundant_static_lifetimes.stderr | 40 +- tests/ui/ref_option_ref.rs | 5 + tests/ui/uninlined_format_args.fixed | 13 + tests/ui/uninlined_format_args.rs | 13 + tests/ui/uninlined_format_args.stderr | 40 +- ...nlined_format_args_panic.edition2018.fixed | 29 + ...lined_format_args_panic.edition2018.stderr | 15 + ...nlined_format_args_panic.edition2021.fixed | 29 + ...lined_format_args_panic.edition2021.stderr | 51 + tests/ui/uninlined_format_args_panic.rs | 29 + tests/ui/unnecessary_cast.fixed | 4 + tests/ui/unnecessary_cast.rs | 4 + tests/ui/unnecessary_to_owned.fixed | 2 +- tests/ui/unnecessary_to_owned.rs | 2 +- tests/ui/unnested_or_patterns.fixed | 16 +- tests/ui/unnested_or_patterns.rs | 16 +- tests/ui/unnested_or_patterns.stderr | 13 +- tests/ui/unused_format_specs.fixed | 18 + tests/ui/unused_format_specs.rs | 18 + tests/ui/unused_format_specs.stderr | 54 + tests/ui/unused_format_specs_unfixable.rs | 30 + tests/ui/unused_format_specs_unfixable.stderr | 69 + tests/ui/use_self.fixed | 33 + tests/ui/use_self.rs | 33 + tests/ui/use_self.stderr | 90 +- tests/versioncheck.rs | 2 +- util/gh-pages/index.html | 6 + util/gh-pages/script.js | 15 +- 284 files changed, 8511 insertions(+), 4206 deletions(-) create mode 100644 clippy_lints/src/casts/as_ptr_cast_mut.rs create mode 100644 clippy_lints/src/casts/cast_nan_to_int.rs create mode 100644 clippy_lints/src/matches/manual_filter.rs create mode 100644 clippy_lints/src/matches/manual_utils.rs create mode 100644 clippy_lints/src/missing_trait_methods.rs create mode 100644 clippy_lints/src/partial_pub_fields.rs create mode 100644 clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs create mode 100644 clippy_lints/src/utils/internal_lints/collapsible_calls.rs create mode 100644 clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs create mode 100644 clippy_lints/src/utils/internal_lints/if_chain_style.rs create mode 100644 clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs create mode 100644 clippy_lints/src/utils/internal_lints/invalid_paths.rs create mode 100644 clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs create mode 100644 clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs create mode 100644 clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs create mode 100644 clippy_lints/src/utils/internal_lints/produce_ice.rs create mode 100644 clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs create mode 100644 clippy_utils/src/mir/maybe_storage_live.rs create mode 100644 clippy_utils/src/mir/mod.rs create mode 100644 clippy_utils/src/mir/possible_borrower.rs create mode 100644 clippy_utils/src/mir/possible_origin.rs create mode 100644 clippy_utils/src/mir/transitive_relation.rs create mode 100644 src/docs/as_ptr_cast_mut.txt create mode 100644 src/docs/cast_nan_to_int.txt create mode 100644 src/docs/manual_filter.txt create mode 100644 src/docs/missing_trait_methods.txt create mode 100644 src/docs/partial_pub_fields.txt create mode 100644 src/docs/unused_format_specs.txt create mode 100644 tests/ui-internal/unnecessary_def_path_hardcoded_path.rs create mode 100644 tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr create mode 100644 tests/ui/as_ptr_cast_mut.rs create mode 100644 tests/ui/as_ptr_cast_mut.stderr create mode 100644 tests/ui/box_default.fixed create mode 100644 tests/ui/box_default_no_std.rs create mode 100644 tests/ui/cast_nan_to_int.rs create mode 100644 tests/ui/cast_nan_to_int.stderr create mode 100644 tests/ui/crashes/ice-9625.rs create mode 100644 tests/ui/from_over_into.fixed create mode 100644 tests/ui/from_over_into_unfixable.rs create mode 100644 tests/ui/from_over_into_unfixable.stderr create mode 100644 tests/ui/manual_filter.fixed create mode 100644 tests/ui/manual_filter.rs create mode 100644 tests/ui/manual_filter.stderr delete mode 100644 tests/ui/match_wild_err_arm.edition2021.stderr rename tests/ui/{match_wild_err_arm.edition2018.stderr => match_wild_err_arm.stderr} (86%) delete mode 100644 tests/ui/min_rust_version_multiple_inner_attr.rs delete mode 100644 tests/ui/min_rust_version_multiple_inner_attr.stderr delete mode 100644 tests/ui/min_rust_version_no_patch.rs delete mode 100644 tests/ui/min_rust_version_outer_attr.rs delete mode 100644 tests/ui/min_rust_version_outer_attr.stderr create mode 100644 tests/ui/missing_trait_methods.rs create mode 100644 tests/ui/missing_trait_methods.stderr create mode 100644 tests/ui/partial_pub_fields.rs create mode 100644 tests/ui/partial_pub_fields.stderr create mode 100644 tests/ui/uninlined_format_args_panic.edition2018.fixed create mode 100644 tests/ui/uninlined_format_args_panic.edition2018.stderr create mode 100644 tests/ui/uninlined_format_args_panic.edition2021.fixed create mode 100644 tests/ui/uninlined_format_args_panic.edition2021.stderr create mode 100644 tests/ui/uninlined_format_args_panic.rs create mode 100644 tests/ui/unused_format_specs.fixed create mode 100644 tests/ui/unused_format_specs.rs create mode 100644 tests/ui/unused_format_specs.stderr create mode 100644 tests/ui/unused_format_specs_unfixable.rs create mode 100644 tests/ui/unused_format_specs_unfixable.stderr diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index fac2c99714d9..b99213011971 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -25,6 +25,7 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 + CARGO_UNSTABLE_SPARSE_REGISTRY: true jobs: base: diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 30607af49012..6448b2d4068d 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -11,6 +11,7 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 + CARGO_UNSTABLE_SPARSE_REGISTRY: true defaults: run: diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 22051093c9cf..14f20212adda 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -15,6 +15,8 @@ on: env: RUST_BACKTRACE: 1 + CARGO_INCREMENTAL: 0 + CARGO_UNSTABLE_SPARSE_REGISTRY: true jobs: clippy_dev: diff --git a/CHANGELOG.md b/CHANGELOG.md index 42615179f705..2d7bda27e4fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3735,6 +3735,7 @@ Released 2018-09-13 [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions +[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut [`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states @@ -3772,6 +3773,7 @@ Released 2018-09-13 [`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless +[`cast_nan_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_nan_to_int [`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation [`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap [`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss @@ -3988,6 +3990,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp +[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map @@ -4046,6 +4049,7 @@ Released 2018-09-13 [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop +[`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals [`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression @@ -4131,6 +4135,7 @@ Released 2018-09-13 [`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap +[`partial_pub_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#partial_pub_fields [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite @@ -4312,6 +4317,7 @@ Released 2018-09-13 [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice [`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect +[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c977b2cacab..85f94a74ad91 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,7 +29,7 @@ All contributors are expected to follow the [Rust Code of Conduct]. ## The Clippy book -If you're new to Clippy and don't know where to start the [Clippy book] includes +If you're new to Clippy and don't know where to start, the [Clippy book] includes a [developer guide] and is a good place to start your journey. [Clippy book]: https://doc.rust-lang.org/nightly/clippy/index.html diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index b1e843bc7f4c..3c3f368a529b 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -478,8 +478,27 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { ``` Once the `msrv` is added to the lint, a relevant test case should be added to -`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted -if the project's MSRV is lower. +the lint's test file, `tests/ui/manual_strip.rs` in this example. It should +have a case for the version below the MSRV and one with the same contents but +for the MSRV version itself. + +```rust +#![feature(custom_inner_attributes)] + +... + +fn msrv_1_44() { + #![clippy::msrv = "1.44"] + + /* something that would trigger the lint */ +} + +fn msrv_1_45() { + #![clippy::msrv = "1.45"] + + /* something that would trigger the lint */ +} +``` As a last step, the lint should be added to the lint documentation. This is done in `clippy_lints/src/utils/conf.rs`: diff --git a/book/src/development/basics.md b/book/src/development/basics.md index 44ba6e32755e..6fb53236e6f1 100644 --- a/book/src/development/basics.md +++ b/book/src/development/basics.md @@ -69,7 +69,7 @@ the reference file with: cargo dev bless ``` -For example, this is necessary, if you fix a typo in an error message of a lint +For example, this is necessary if you fix a typo in an error message of a lint, or if you modify a test file to add a test case. > _Note:_ This command may update more files than you intended. In that case @@ -101,8 +101,9 @@ cargo dev setup intellij cargo dev dogfood ``` -More about intellij command usage and reasons -[here](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust) +More about [intellij] command usage and reasons. + +[intellij]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust ## lintcheck diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index 2e0794f12fa1..535c25e69f1b 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -49,7 +49,7 @@ fn mtime(path: impl AsRef) -> SystemTime { .into_iter() .flatten() .flatten() - .map(|entry| mtime(&entry.path())) + .map(|entry| mtime(entry.path())) .max() .unwrap_or(SystemTime::UNIX_EPOCH) } else { diff --git a/clippy_dev/src/setup/intellij.rs b/clippy_dev/src/setup/intellij.rs index b64e79733eb2..efdb158c21e9 100644 --- a/clippy_dev/src/setup/intellij.rs +++ b/clippy_dev/src/setup/intellij.rs @@ -36,9 +36,8 @@ impl ClippyProjectInfo { } pub fn setup_rustc_src(rustc_path: &str) { - let rustc_source_dir = match check_and_get_rustc_dir(rustc_path) { - Ok(path) => path, - Err(_) => return, + let Ok(rustc_source_dir) = check_and_get_rustc_dir(rustc_path) else { + return }; for project in CLIPPY_PROJECTS { @@ -172,14 +171,10 @@ pub fn remove_rustc_src() { } fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool { - let mut cargo_content = if let Ok(content) = read_project_file(project.cargo_file) { - content - } else { + let Ok(mut cargo_content) = read_project_file(project.cargo_file) else { return false; }; - let section_start = if let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) { - section_start - } else { + let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) else { println!( "info: dependencies could not be found in `{}` for {}, skipping file", project.cargo_file, project.name @@ -187,9 +182,7 @@ fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool { return true; }; - let end_point = if let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) { - end_point - } else { + let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) else { eprintln!( "error: the end of the rustc dependencies section could not be found in `{}`", project.cargo_file diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 0eb443167ecf..e690bc369cd4 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -128,7 +128,7 @@ fn generate_lint_files( for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { let content = gen_lint_group_list(&lint_group, lints.iter()); process_file( - &format!("clippy_lints/src/lib.register_{lint_group}.rs"), + format!("clippy_lints/src/lib.register_{lint_group}.rs"), update_mode, &content, ); @@ -869,13 +869,11 @@ fn clippy_lints_src_files() -> impl Iterator { macro_rules! match_tokens { ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => { { - $($(let $capture =)? if let Some(LintDeclSearchResult { + $(#[allow(clippy::redundant_pattern)] let Some(LintDeclSearchResult { token_kind: TokenKind::$token $({$($fields)*})?, - content: _x, + content: $($capture @)? _, .. - }) = $iter.next() { - _x - } else { + }) = $iter.next() else { continue; };)* #[allow(clippy::unused_unit)] diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 2a15cbc7a3c3..08164c0b654e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::eq_expr_value; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{eq_expr_value, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -483,7 +483,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) + cx.tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) } struct NotSimplificationVisitor<'a, 'tcx> { diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 792183ac4081..36daceabe0be 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -1,5 +1,12 @@ -use clippy_utils::{diagnostics::span_lint_and_help, is_default_equivalent, path_def_id}; -use rustc_hir::{Expr, ExprKind, QPath}; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, get_parent_node, is_default_equivalent, macros::macro_backtrace, match_path, + path_def_id, paths, ty::expr_sig, +}; +use rustc_errors::Applicability; +use rustc_hir::{ + intravisit::{walk_ty, Visitor}, + Block, Expr, ExprKind, Local, Node, QPath, TyKind, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -15,12 +22,6 @@ declare_clippy_lint! { /// Second, `Box::default()` can be faster /// [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). /// - /// ### Known problems - /// The lint may miss some cases (e.g. Box::new(String::from(""))). - /// On the other hand, it will trigger on cases where the `default` - /// code comes from a macro that does something different based on - /// e.g. target operating system. - /// /// ### Example /// ```rust /// let x: Box = Box::new(Default::default()); @@ -41,21 +42,88 @@ impl LateLintPass<'_> for BoxDefault { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Call(box_new, [arg]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_new.kind - && let ExprKind::Call(..) = arg.kind + && let ExprKind::Call(arg_path, ..) = arg.kind && !in_external_macro(cx.sess(), expr.span) - && expr.span.eq_ctxt(arg.span) + && (expr.span.eq_ctxt(arg.span) || is_vec_expn(cx, arg)) && seg.ident.name == sym::new - && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box() + && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) && is_default_equivalent(cx, arg) { - span_lint_and_help( + let arg_ty = cx.typeck_results().expr_ty(arg); + span_lint_and_sugg( cx, BOX_DEFAULT, expr.span, "`Box::new(_)` of default value", - None, - "use `Box::default()` instead", + "try", + if is_plain_default(arg_path) || given_type(cx, expr) { + "Box::default()".into() + } else { + format!("Box::<{arg_ty}>::default()") + }, + Applicability::MachineApplicable ); } } } + +fn is_plain_default(arg_path: &Expr<'_>) -> bool { + // we need to match the actual path so we don't match e.g. "u8::default" + if let ExprKind::Path(QPath::Resolved(None, path)) = &arg_path.kind { + // avoid generic parameters + match_path(path, &paths::DEFAULT_TRAIT_METHOD) && path.segments.iter().all(|seg| seg.args.is_none()) + } else { + false + } +} + +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)) +} + +#[derive(Default)] +struct InferVisitor(bool); + +impl<'tcx> Visitor<'tcx> for InferVisitor { + fn visit_ty(&mut self, t: &rustc_hir::Ty<'_>) { + self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..)); + if !self.0 { + walk_ty(self, t); + } + } +} + +fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Local(Local { ty: Some(ty), .. })) => { + let mut v = InferVisitor::default(); + v.visit_ty(ty); + !v.0 + }, + Some( + Node::Expr(Expr { + kind: ExprKind::Call(path, args), + .. + }) | Node::Block(Block { + expr: + Some(Expr { + kind: ExprKind::Call(path, args), + .. + }), + .. + }), + ) => { + if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) && + let Some(sig) = expr_sig(cx, path) && + let Some(input) = sig.input(index) + { + input.no_bound_vars().is_some() + } else { + false + } + }, + _ => false, + } +} diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs new file mode 100644 index 000000000000..9409f4844f54 --- /dev/null +++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::{ + mir::Mutability, + ty::{self, Ty, TypeAndMut}, +}; + +use super::AS_PTR_CAST_MUT; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) { + if let ty::RawPtr(ptrty @ TypeAndMut { mutbl: Mutability::Mut, .. }) = cast_to.kind() + && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = + cx.typeck_results().node_type(cast_expr.hir_id).kind() + && let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind + && method_name.ident.name == rustc_span::sym::as_ptr + && let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id) + && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did) + && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() + && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() + && let Some(recv) = snippet_opt(cx, receiver.span) + { + // `as_mut_ptr` might not exist + let applicability = Applicability::MaybeIncorrect; + + span_lint_and_sugg( + cx, + AS_PTR_CAST_MUT, + expr.span, + &format!("casting the result of `as_ptr` to *{ptrty}"), + "replace with", + format!("{recv}.as_mut_ptr()"), + applicability + ); + } +} diff --git a/clippy_lints/src/casts/cast_nan_to_int.rs b/clippy_lints/src/casts/cast_nan_to_int.rs new file mode 100644 index 000000000000..322dc41b3a19 --- /dev/null +++ b/clippy_lints/src/casts/cast_nan_to_int.rs @@ -0,0 +1,28 @@ +use super::CAST_NAN_TO_INT; + +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_note; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, from_ty: Ty<'_>, to_ty: Ty<'_>) { + if from_ty.is_floating_point() && to_ty.is_integral() && is_known_nan(cx, cast_expr) { + span_lint_and_note( + cx, + CAST_NAN_TO_INT, + expr.span, + &format!("casting a known NaN to {to_ty}"), + None, + "this always evaluates to 0", + ); + } +} + +fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + match constant(cx, cx.typeck_results(), e) { + Some((Constant::F64(n), _)) => n.is_nan(), + Some((Constant::F32(n), _)) => n.is_nan(), + _ => false, + } +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index cc5d346b954e..b72c4c772f1c 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -1,8 +1,10 @@ +mod as_ptr_cast_mut; mod as_underscore; mod borrow_as_ptr; mod cast_abs_to_unsigned; mod cast_enum_constructor; mod cast_lossless; +mod cast_nan_to_int; mod cast_possible_truncation; mod cast_possible_wrap; mod cast_precision_loss; @@ -569,6 +571,7 @@ declare_clippy_lint! { pedantic, "borrowing just to cast to a raw pointer" } + declare_clippy_lint! { /// ### What it does /// Checks for a raw slice being cast to a slice pointer @@ -596,6 +599,54 @@ declare_clippy_lint! { "casting a slice created from a pointer and length to a slice pointer" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer + /// + /// ### Why is this bad? + /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior + /// mutability is used, making it unlikely that having it as a mutable pointer is correct. + /// + /// ### Example + /// ```rust + /// let string = String::with_capacity(1); + /// let ptr = string.as_ptr() as *mut u8; + /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR + /// ``` + /// Use instead: + /// ```rust + /// let mut string = String::with_capacity(1); + /// let ptr = string.as_mut_ptr(); + /// unsafe { ptr.write(4) }; + /// ``` + #[clippy::version = "1.66.0"] + pub AS_PTR_CAST_MUT, + nursery, + "casting the result of the `&self`-taking `as_ptr` to a mutabe pointer" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for a known NaN float being cast to an integer + /// + /// ### Why is this bad? + /// NaNs are cast into zero, so one could simply use this and make the + /// code more readable. The lint could also hint at a programmer error. + /// + /// ### Example + /// ```rust,ignore + /// let _: (0.0_f32 / 0.0) as u64; + /// ``` + /// Use instead: + /// ```rust,ignore + /// let _: = 0_u64; + /// ``` + #[clippy::version = "1.64.0"] + pub CAST_NAN_TO_INT, + suspicious, + "casting a known floating-point NaN into an integer" +} + pub struct Casts { msrv: Option, } @@ -627,7 +678,9 @@ impl_lint_pass!(Casts => [ CAST_ABS_TO_UNSIGNED, AS_UNDERSCORE, BORROW_AS_PTR, - CAST_SLICE_FROM_RAW_PARTS + CAST_SLICE_FROM_RAW_PARTS, + AS_PTR_CAST_MUT, + CAST_NAN_TO_INT, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -653,6 +706,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv); + as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); @@ -664,6 +718,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); } cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 21ed7f4844cc..c8596987e4d7 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -59,9 +59,6 @@ pub(super) fn check<'tcx>( lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); return false; }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => { - return false; - }, LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index a05b41eb3ab5..0fe973b49a35 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq}; +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_span::sym; declare_clippy_lint! { /// ### What it does @@ -106,7 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { // Check that the type being compared implements `core::cmp::Ord` let ty = cx.typeck_results().expr_ty(lhs1); - let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])); + let is_ord = cx + .tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])); if !is_ord { return; diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 3ed9cd36a229..03460689e19a 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; +use clippy_utils::{get_parent_node, numeric_literal}; use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, walk_stmt, Visitor}, - Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, + Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{ @@ -55,22 +55,31 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { - let mut visitor = NumericFallbackVisitor::new(cx); + let is_parent_const = if let Some(Node::Item(item)) = get_parent_node(cx.tcx, body.id().hir_id) { + matches!(item.kind, ItemKind::Const(..)) + } else { + false + }; + let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const); visitor.visit_body(body); } } struct NumericFallbackVisitor<'a, 'tcx> { /// Stack manages type bound of exprs. The top element holds current expr type. - ty_bounds: Vec>, + ty_bounds: Vec, cx: &'a LateContext<'tcx>, } impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>, is_parent_const: bool) -> Self { Self { - ty_bounds: vec![TyBound::Nothing], + ty_bounds: vec![if is_parent_const { + ExplicitTyBound(true) + } else { + ExplicitTyBound(false) + }], cx, } } @@ -79,10 +88,9 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { if_chain! { if !in_external_macro(self.cx.sess(), lit.span); - if let Some(ty_bound) = self.ty_bounds.last(); + if matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - if !ty_bound.is_numeric(); then { let (suffix, is_float) = match lit_ty.kind() { ty::Int(IntTy::I32) => ("i32", false), @@ -123,7 +131,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) { for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) { // Push found arg type, then visit arg. - self.ty_bounds.push(TyBound::Ty(*bound)); + self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); } @@ -135,7 +143,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) { let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder(); for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) { - self.ty_bounds.push(TyBound::Ty(*bound)); + self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); } @@ -169,7 +177,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { // Visit base with no bound. if let Some(base) = base { - self.ty_bounds.push(TyBound::Nothing); + self.ty_bounds.push(ExplicitTyBound(false)); self.visit_expr(base); self.ty_bounds.pop(); } @@ -192,15 +200,10 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(local) => { - if local.ty.is_some() { - self.ty_bounds.push(TyBound::Any); - } else { - self.ty_bounds.push(TyBound::Nothing); - } - }, + // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` + StmtKind::Local(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())), - _ => self.ty_bounds.push(TyBound::Nothing), + _ => self.ty_bounds.push(ExplicitTyBound(false)), } walk_stmt(self, stmt); @@ -218,28 +221,18 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option { - Any, - Ty(Ty<'tcx>), - Nothing, -} +struct ExplicitTyBound(pub bool); -impl<'tcx> TyBound<'tcx> { - fn is_numeric(self) -> bool { - match self { - TyBound::Any => true, - TyBound::Ty(t) => t.is_numeric(), - TyBound::Nothing => false, - } +impl<'tcx> From> for ExplicitTyBound { + fn from(v: Ty<'tcx>) -> Self { + Self(v.is_numeric()) } } -impl<'tcx> From>> for TyBound<'tcx> { +impl<'tcx> From>> for ExplicitTyBound { fn from(v: Option>) -> Self { - match v { - Some(t) => TyBound::Ty(t), - None => TyBound::Nothing, - } + Self(v.map_or(false, Ty::is_numeric)) } } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 02a16f765b73..a95d9f5390de 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; @@ -11,13 +12,16 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, - GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, - Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp, + self as hir, + def_id::{DefId, LocalDefId}, + BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, ImplItem, + ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem, + TraitItemKind, TyKind, UnOp, }; use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ self, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, @@ -141,7 +145,7 @@ declare_clippy_lint! { "dereferencing when the compiler would automatically dereference" } -impl_lint_pass!(Dereferencing => [ +impl_lint_pass!(Dereferencing<'_> => [ EXPLICIT_DEREF_METHODS, NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE, @@ -149,7 +153,7 @@ impl_lint_pass!(Dereferencing => [ ]); #[derive(Default)] -pub struct Dereferencing { +pub struct Dereferencing<'tcx> { state: Option<(State, StateData)>, // While parsing a `deref` method call in ufcs form, the path to the function is itself an @@ -170,11 +174,16 @@ pub struct Dereferencing { /// e.g. `m!(x) | Foo::Bar(ref x)` ref_locals: FxIndexMap>, + /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by + /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead + /// be moved. + possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + // `IntoIterator` for arrays requires Rust 1.53. msrv: Option, } -impl Dereferencing { +impl<'tcx> Dereferencing<'tcx> { #[must_use] pub fn new(msrv: Option) -> Self { Self { @@ -244,7 +253,7 @@ struct RefPat { hir_id: HirId, } -impl<'tcx> LateLintPass<'tcx> for Dereferencing { +impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Skip path expressions from deref calls. e.g. `Deref::deref(e)` @@ -278,7 +287,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, expr, self.msrv); + let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv); match kind { RefOp::Deref => { if let Position::FieldAccess { @@ -550,6 +559,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { } fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| { + local_def_id == cx.tcx.hir().body_owner_def_id(body.id()) + }) { + self.possible_borrowers.pop(); + } + if Some(body.id()) == self.current_body { for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { let replacements = pat.replacements; @@ -682,6 +697,7 @@ impl Position { #[expect(clippy::too_many_lines)] fn walk_parents<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, e: &'tcx Expr<'_>, msrv: Option, ) -> (Position, &'tcx [Adjustment<'tcx>]) { @@ -796,7 +812,16 @@ fn walk_parents<'tcx>( Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), None => { if let ty::Param(param_ty) = ty.skip_binder().kind() { - needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv) + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i, + *param_ty, + e, + precedence, + msrv, + ) } else { ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) .position_for_arg() @@ -843,7 +868,16 @@ fn walk_parents<'tcx>( args.iter().position(|arg| arg.hir_id == child_id).map(|i| { let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1]; if let ty::Param(param_ty) = ty.kind() { - needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv) + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i + 1, + *param_ty, + e, + precedence, + msrv, + ) } else { ty_auto_deref_stability( cx, @@ -1017,8 +1051,10 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { // If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. // The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to // be moved, but it cannot be. +#[expect(clippy::too_many_arguments)] fn needless_borrow_impl_arg_position<'tcx>( cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, parent: &Expr<'tcx>, arg_index: usize, param_ty: ParamTy, @@ -1081,10 +1117,13 @@ fn needless_borrow_impl_arg_position<'tcx>( // elements are modified each time `check_referent` is called. let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); - let mut check_referent = |referent| { + let mut check_reference_and_referent = |reference, referent| { let referent_ty = cx.typeck_results().expr_ty(referent); - if !is_copy(cx, referent_ty) { + if !is_copy(cx, referent_ty) + && (referent_ty.has_significant_drop(cx.tcx, cx.param_env) + || !referent_used_exactly_once(cx, possible_borrowers, reference)) + { return false; } @@ -1125,7 +1164,7 @@ fn needless_borrow_impl_arg_position<'tcx>( let mut needless_borrow = false; while let ExprKind::AddrOf(_, _, referent) = expr.kind { - if !check_referent(referent) { + if !check_reference_and_referent(expr, referent) { break; } expr = referent; @@ -1153,6 +1192,36 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { }) } +fn referent_used_exactly_once<'tcx>( + cx: &LateContext<'tcx>, + possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, + reference: &Expr<'tcx>, +) -> bool { + let mir = enclosing_mir(cx.tcx, reference.hir_id); + if let Some(local) = expr_local(cx.tcx, reference) + && let [location] = *local_assignments(mir, local).as_slice() + && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind + && !place.has_deref() + { + let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); + if possible_borrowers + .last() + .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id) + { + possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); + } + let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; + // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is + // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of + // itself. See the comment in that method for an explanation as to why. + possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) + && used_exactly_once(mir, place.local).unwrap_or(false) + } else { + false + } +} + // Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting // projected type that is a type parameter. Returns `false` if replacing the types would have an // effect on the function signature beyond substituting `new_ty` for `param_ty`. @@ -1437,8 +1506,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data } } -impl Dereferencing { - fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { +impl<'tcx> Dereferencing<'tcx> { + fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 3fac93dcc90c..fad984d05ca9 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -339,10 +339,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, }; - let copy_id = match cx.tcx.lang_items().copy_trait() { - Some(id) => id, - None => return, - }; + let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { return }; let (ty_adt, ty_subs) = match *ty.kind() { // Unions can't derive clone. ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 1a381f92c031..6ac85606d9c7 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -94,9 +94,8 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { } else { path_def_id(cx, expr) }; - let def_id = match uncalled_path.or_else(|| fn_def_id(cx, expr)) { - Some(def_id) => def_id, - None => return, + let Some(def_id) = uncalled_path.or_else(|| fn_def_id(cx, expr)) else { + return }; let conf = match self.disallowed.get(&def_id) { Some(&index) => &self.conf_disallowed[index], diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 9c834cf01448..b44e62435881 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -65,28 +65,24 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) { - Some(higher::If { cond, then, r#else }) => (cond, then, r#else), - _ => return, + let Some(higher::If { cond: cond_expr, then: then_expr, r#else: else_expr }) = higher::If::hir(expr) else { + return }; - let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) { - Some(x) => x, - None => return, + let Some((map_ty, contains_expr)) = try_parse_contains(cx, cond_expr) else { + return }; - let then_search = match find_insert_calls(cx, &contains_expr, then_expr) { - Some(x) => x, - None => return, + let Some(then_search) = find_insert_calls(cx, &contains_expr, then_expr) else { + return }; let mut app = Applicability::MachineApplicable; let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; let sugg = if let Some(else_expr) = else_expr { - let else_search = match find_insert_calls(cx, &contains_expr, else_expr) { - Some(search) => search, - None => return, + let Some(else_search) = find_insert_calls(cx, &contains_expr, else_expr) else { + return; }; if then_search.edits.is_empty() && else_search.edits.is_empty() { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 3732410e71e5..7b9786d7e570 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -213,9 +213,8 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tc if !closure_ty.has_late_bound_regions() { return true; } - let substs = match closure_ty.kind() { - ty::Closure(_, substs) => substs, - _ => return false, + let ty::Closure(_, substs) = closure_ty.kind() else { + return false; }; let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal); cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 99bef62f8143..32073536b45b 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; -use clippy_utils::macros::{is_format_macro, FormatArgsExpn, FormatParam, FormatParamUsage}; +use clippy_utils::macros::{ + is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage, +}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; use if_chain::if_chain; use itertools::Itertools; @@ -13,6 +15,8 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::def_id::DefId; +use rustc_span::edition::Edition::Edition2021; use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol}; declare_clippy_lint! { @@ -111,11 +115,47 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.65.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } -impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); +declare_clippy_lint! { + /// ### What it does + /// Detects [formatting parameters] that have no effect on the output of + /// `format!()`, `println!()` or similar macros. + /// + /// ### Why is this bad? + /// Shorter format specifiers are easier to read, it may also indicate that + /// an expected formatting operation such as adding padding isn't happening. + /// + /// ### Example + /// ```rust + /// println!("{:.}", 1.0); + /// + /// println!("not padded: {:5}", format_args!("...")); + /// ``` + /// Use instead: + /// ```rust + /// println!("{}", 1.0); + /// + /// println!("not padded: {}", format_args!("...")); + /// // OR + /// println!("padded: {:5}", format!("...")); + /// ``` + /// + /// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters + #[clippy::version = "1.66.0"] + pub UNUSED_FORMAT_SPECS, + complexity, + "use of a format specifier that has no effect" +} + +impl_lint_pass!(FormatArgs => [ + FORMAT_IN_FORMAT_ARGS, + TO_STRING_IN_FORMAT_ARGS, + UNINLINED_FORMAT_ARGS, + UNUSED_FORMAT_SPECS, +]); pub struct FormatArgs { msrv: Option, @@ -130,27 +170,26 @@ impl FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let Some(format_args) = FormatArgsExpn::parse(cx, expr); - let expr_expn_data = expr.span.ctxt().outer_expn_data(); - let outermost_expn_data = outermost_expn_data(expr_expn_data); - if let Some(macro_def_id) = outermost_expn_data.macro_def_id; - if is_format_macro(cx, macro_def_id); - if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; - then { - for arg in &format_args.args { - if !arg.format.is_default() { - continue; - } - if is_aliased(&format_args, arg.param.value.hir_id) { - continue; - } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); - check_to_string_in_format_args(cx, name, arg.param.value); + if let Some(format_args) = FormatArgsExpn::parse(cx, expr) + && let expr_expn_data = expr.span.ctxt().outer_expn_data() + && let outermost_expn_data = outermost_expn_data(expr_expn_data) + && let Some(macro_def_id) = outermost_expn_data.macro_def_id + && is_format_macro(cx, macro_def_id) + && let ExpnKind::Macro(_, name) = outermost_expn_data.kind + { + for arg in &format_args.args { + check_unused_format_specifier(cx, arg); + if !arg.format.is_default() { + continue; } - if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, &format_args, outermost_expn_data.call_site); + if is_aliased(&format_args, arg.param.value.hir_id) { + continue; } + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); + check_to_string_in_format_args(cx, name, arg.param.value); + } + if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id); } } } @@ -158,10 +197,84 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { extract_msrv_attr!(LateContext); } -fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span) { +fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { + let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs(); + + if let Count::Implied(Some(mut span)) = arg.format.precision + && !span.is_empty() + { + span_lint_and_then( + cx, + UNUSED_FORMAT_SPECS, + span, + "empty precision specifier has no effect", + |diag| { + if param_ty.is_floating_point() { + diag.note("a precision specifier is not required to format floats"); + } + + if arg.format.is_default() { + // If there's no other specifiers remove the `:` too + span = arg.format_span(); + } + + diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable); + }, + ); + } + + if is_type_diagnostic_item(cx, param_ty, sym::Arguments) && !arg.format.is_default_for_trait() { + span_lint_and_then( + cx, + UNUSED_FORMAT_SPECS, + arg.span, + "format specifiers have no effect on `format_args!()`", + |diag| { + let mut suggest_format = |spec, span| { + let message = format!("for the {spec} to apply consider using `format!()`"); + + if let Some(mac_call) = root_macro_call(arg.param.value.span) + && cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id) + && arg.span.eq_ctxt(mac_call.span) + { + diag.span_suggestion( + cx.sess().source_map().span_until_char(mac_call.span, '!'), + message, + "format", + Applicability::MaybeIncorrect, + ); + } else if let Some(span) = span { + diag.span_help(span, message); + } + }; + + if !arg.format.width.is_implied() { + suggest_format("width", arg.format.width.span()); + } + + if !arg.format.precision.is_implied() { + suggest_format("precision", arg.format.precision.span()); + } + + diag.span_suggestion_verbose( + arg.format_span(), + "if the current behavior is intentional, remove the format specifiers", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + } +} + +fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) { if args.format_string.span.from_expansion() { return; } + if call_site.edition() < Edition2021 && is_panic(cx, def_id) { + // panic! before 2021 edition considers a single string argument as non-format + return; + } let mut fixes = Vec::new(); // If any of the arguments are referenced by an index number, @@ -248,7 +361,7 @@ fn check_format_in_format_args( fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) { if_chain! { if !value.span.from_expansion(); - if let ExprKind::MethodCall(_, receiver, [], _) = value.kind; + if let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind; if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id); if is_diag_trait_item(cx, method_def_id, sym::ToString); let receiver_ty = cx.typeck_results().expr_ty(receiver); @@ -264,7 +377,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex span_lint_and_sugg( cx, TO_STRING_IN_FORMAT_ARGS, - value.span.with_lo(receiver.span.hi()), + to_string_span.with_lo(receiver.span.hi()), &format!( "`to_string` applied to a type that implements `Display` in `{name}!` args" ), diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 5d25c1d06341..95eda4ea8827 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,11 +1,19 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::span_is_local; +use clippy_utils::source::snippet_opt; +use clippy_utils::{meets_msrv, msrvs, path_def_id}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_path, Visitor}; +use rustc_hir::{ + GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty, + TyKind, +}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{kw, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -54,28 +62,152 @@ impl FromOverInto { impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { return; } - if_chain! { - if let hir::ItemKind::Impl{ .. } = &item.kind; - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.def_id); - if cx.tcx.is_diagnostic_item(sym::Into, impl_trait_ref.def_id); - - then { - span_lint_and_help( - cx, - FROM_OVER_INTO, - cx.tcx.sess.source_map().guess_head_span(item.span), - "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", - None, - &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()), - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(hir_trait_ref), + self_ty, + items: [impl_item_ref], + .. + }) = item.kind + && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() + // `impl Into for self_ty` + && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args + && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.def_id) + && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) + { + span_lint_and_then( + cx, + FROM_OVER_INTO, + cx.tcx.sess.source_map().guess_head_span(item.span), + "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", + |diag| { + // If the target type is likely foreign mention the orphan rules as it's a common source of confusion + if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) { + diag.help( + "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" + ); + } + + let message = format!("replace the `Into` implentation with `From<{}>`", middle_trait_ref.self_ty()); + if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) { + diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); + } else { + diag.help(message); + } + }, + ); } } extract_msrv_attr!(LateContext); } + +/// Finds the occurences of `Self` and `self` +struct SelfFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + /// Occurences of `Self` + upper: Vec, + /// Occurences of `self` + lower: Vec, + /// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding + /// already named `val` + invalid: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) { + for segment in path.segments { + match segment.ident.name { + kw::SelfLower => self.lower.push(segment.ident.span), + kw::SelfUpper => self.upper.push(segment.ident.span), + _ => continue, + } + } + + self.invalid |= path.span.from_expansion(); + if !self.invalid { + walk_path(self, path); + } + } + + fn visit_name(&mut self, name: Symbol) { + if name == sym::val { + self.invalid = true; + } + } +} + +fn convert_to_from( + cx: &LateContext<'_>, + into_trait_seg: &PathSegment<'_>, + target_ty: &Ty<'_>, + self_ty: &Ty<'_>, + impl_item_ref: &ImplItemRef, +) -> Option> { + let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id); + let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None }; + let body = cx.tcx.hir().body(body_id); + let [input] = body.params else { return None }; + let PatKind::Binding(.., self_ident, None) = input.pat.kind else { return None }; + + let from = snippet_opt(cx, self_ty.span)?; + let into = snippet_opt(cx, target_ty.span)?; + + let mut suggestions = vec![ + // impl Into for U -> impl From for U + // ~~~~ ~~~~ + (into_trait_seg.ident.span, String::from("From")), + // impl Into for U -> impl Into for U + // ~ ~ + (target_ty.span, from.clone()), + // impl Into for U -> impl Into for T + // ~ ~ + (self_ty.span, into), + // fn into(self) -> T -> fn from(self) -> T + // ~~~~ ~~~~ + (impl_item.ident.span, String::from("from")), + // fn into([mut] self) -> T -> fn into([mut] v: T) -> T + // ~~~~ ~~~~ + (self_ident.span, format!("val: {from}")), + // fn into(self) -> T -> fn into(self) -> Self + // ~ ~~~~ + (sig.decl.output.span(), String::from("Self")), + ]; + + let mut finder = SelfFinder { + cx, + upper: Vec::new(), + lower: Vec::new(), + invalid: false, + }; + finder.visit_expr(body.value); + + if finder.invalid { + return None; + } + + // don't try to replace e.g. `Self::default()` with `&[T]::default()` + if !finder.upper.is_empty() && !matches!(self_ty.kind, TyKind::Path(_)) { + return None; + } + + for span in finder.upper { + suggestions.push((span, from.clone())); + } + for span in finder.lower { + suggestions.push((span, String::from("val"))); + } + + Some(suggestions) +} diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index d263804f32cf..3064b6c9d22f 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -7,14 +7,14 @@ use rustc_middle::{ lint::in_external_macro, ty::{self, Ty}, }; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, Symbol}; use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; +use clippy_utils::{return_ty, trait_ref_of_method}; use core::ops::ControlFlow; @@ -181,7 +181,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) } } -static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; +static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc]; fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool { match *ty.kind() { @@ -189,7 +189,9 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, ty::Adt(adt, substs) => { tys.insert(adt.did()) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) - || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did(), path)) + || KNOWN_WRAPPER_TYS + .iter() + .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did())) && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) }, ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)), diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index f83f8b40f94b..bd473ac7e51b 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -22,9 +22,8 @@ pub(super) fn check_fn( return; } - let code_snippet = match snippet_opt(cx, body.value.span) { - Some(s) => s, - _ => return, + let Some(code_snippet) = snippet_opt(cx, body.value.span) else { + return }; let mut line_count: u64 = 0; let mut in_comment = false; diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 48edbf6ae576..29d59c26d92c 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.44.0"] pub IMPLICIT_SATURATING_SUB, - pedantic, + style, "Perform saturating subtraction instead of implicitly checking lower bound of data type" } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 36e03e50a8e4..0ef77e03de90 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -145,9 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind { let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); - let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized { - val - } else { + let Some((rel, normalized_lhs, normalized_rhs)) = normalized else { return; }; diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index eb13d0869c03..8ed7e4bb196c 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -124,9 +124,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { } if let ItemKind::Enum(ref def, _) = item.kind { let ty = cx.tcx.type_of(item.def_id); - let (adt, subst) = match ty.kind() { - Adt(adt, subst) => (adt, subst), - _ => panic!("already checked whether this is an enum"), + let Adt(adt, subst) = ty.kind() else { + panic!("already checked whether this is an enum") }; if adt.variants().len() <= 1 { return; diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 176787497ebf..b7798b1c1d74 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_must_use_ty, match_type}; +use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type}; use clippy_utils::{is_must_use_func_call, paths}; use if_chain::if_chain; use rustc_hir::{Local, PatKind}; @@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Symbol}; declare_clippy_lint! { /// ### What it does @@ -99,10 +100,9 @@ declare_clippy_lint! { declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); -const SYNC_GUARD_PATHS: [&[&str]; 6] = [ - &paths::MUTEX_GUARD, - &paths::RWLOCK_READ_GUARD, - &paths::RWLOCK_WRITE_GUARD, +const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard]; + +const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::PARKING_LOT_MUTEX_GUARD, &paths::PARKING_LOT_RWLOCK_READ_GUARD, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD, @@ -121,7 +121,10 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { GenericArgKind::Type(inner_ty) => { - SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) + SYNC_GUARD_SYMS + .iter() + .any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym)) + || SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) }, GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, @@ -134,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "non-binding let on a synchronization lock", None, "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`" + binding or dropping explicitly with `std::mem::drop`", ); } else if init_ty.needs_drop(cx.tcx, cx.param_env) { span_lint_and_help( @@ -144,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "non-binding `let` on a type that implements `Drop`", None, "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`" + binding or dropping explicitly with `std::mem::drop`", ); } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { span_lint_and_help( @@ -153,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { local.span, "non-binding let on an expression with `#[must_use]` type", None, - "consider explicitly using expression value" + "consider explicitly using expression value", ); } else if is_must_use_func_call(cx, init) { span_lint_and_help( @@ -162,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { local.span, "non-binding let on a result of a `#[must_use]` function", None, - "consider explicitly using function result" + "consider explicitly using function result", ); } } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index fe1f0b56646c..f5ad52ba1892 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -25,6 +25,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), + LintId::of(casts::CAST_NAN_TO_INT), LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), @@ -71,6 +72,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(format::USELESS_FORMAT), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), + LintId::of(format_args::UNUSED_FORMAT_SPECS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), LintId::of(formatting::POSSIBLE_MISSING_COMMA), @@ -87,6 +90,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), + LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(infinite_iter::INFINITE_ITER), LintId::of(inherent_to_string::INHERENT_TO_STRING), @@ -136,6 +140,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(match_result_ok::MATCH_RESULT_OK), LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_FILTER), LintId::of(matches::MANUAL_MAP), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index a58d066fa6b6..8be9dc4baf19 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -13,6 +13,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(double_parens::DOUBLE_PARENS), LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), + LintId::of(format_args::UNUSED_FORMAT_SPECS), LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(int_plus_one::INT_PLUS_ONE), LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), @@ -27,6 +28,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(manual_strip::MANUAL_STRIP), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::MANUAL_FILTER), LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_SINGLE_BINDING), diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs index 71dfdab369b9..40c94c6e8d33 100644 --- a/clippy_lints/src/lib.register_internal.rs +++ b/clippy_lints/src/lib.register_internal.rs @@ -3,20 +3,20 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), - LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS), - LintId::of(utils::internal_lints::DEFAULT_DEPRECATION_REASON), - LintId::of(utils::internal_lints::DEFAULT_LINT), - LintId::of(utils::internal_lints::IF_CHAIN_STYLE), - LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL), - LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::INVALID_PATHS), - LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL), - LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), - LintId::of(utils::internal_lints::PRODUCE_ICE), - LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH), - LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), + LintId::of(utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL), + LintId::of(utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS), + LintId::of(utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS), + LintId::of(utils::internal_lints::if_chain_style::IF_CHAIN_STYLE), + LintId::of(utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL), + LintId::of(utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR), + LintId::of(utils::internal_lints::invalid_paths::INVALID_PATHS), + LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON), + LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT), + LintId::of(utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE), + LintId::of(utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS), + LintId::of(utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE), + LintId::of(utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL), + LintId::of(utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA), + LintId::of(utils::internal_lints::produce_ice::PRODUCE_ICE), + LintId::of(utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH), ]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 306cb6a61c94..800e3a876713 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -4,37 +4,37 @@ store.register_lints(&[ #[cfg(feature = "internal")] - utils::internal_lints::CLIPPY_LINTS_INTERNAL, + utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL, #[cfg(feature = "internal")] - utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, #[cfg(feature = "internal")] - utils::internal_lints::COMPILER_LINT_FUNCTIONS, + utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS, #[cfg(feature = "internal")] - utils::internal_lints::DEFAULT_DEPRECATION_REASON, + utils::internal_lints::if_chain_style::IF_CHAIN_STYLE, #[cfg(feature = "internal")] - utils::internal_lints::DEFAULT_LINT, + utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL, #[cfg(feature = "internal")] - utils::internal_lints::IF_CHAIN_STYLE, + utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR, #[cfg(feature = "internal")] - utils::internal_lints::INTERNING_DEFINED_SYMBOL, + utils::internal_lints::invalid_paths::INVALID_PATHS, #[cfg(feature = "internal")] - utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE, + utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON, #[cfg(feature = "internal")] - utils::internal_lints::INVALID_PATHS, + utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT, #[cfg(feature = "internal")] - utils::internal_lints::LINT_WITHOUT_LINT_PASS, + utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] - utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, + utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal")] - utils::internal_lints::MISSING_MSRV_ATTR_IMPL, + utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE, #[cfg(feature = "internal")] - utils::internal_lints::OUTER_EXPN_EXPN_DATA, + utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, #[cfg(feature = "internal")] - utils::internal_lints::PRODUCE_ICE, + utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, #[cfg(feature = "internal")] - utils::internal_lints::UNNECESSARY_DEF_PATH, + utils::internal_lints::produce_ice::PRODUCE_ICE, #[cfg(feature = "internal")] - utils::internal_lints::UNNECESSARY_SYMBOL_STR, + utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH, almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, as_conversions::AS_CONVERSIONS, @@ -66,12 +66,14 @@ store.register_lints(&[ cargo::NEGATIVE_FEATURE_NAMES, cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, + casts::AS_PTR_CAST_MUT, casts::AS_UNDERSCORE, casts::BORROW_AS_PTR, casts::CAST_ABS_TO_UNSIGNED, casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_TRUNCATION, casts::CAST_LOSSLESS, + casts::CAST_NAN_TO_INT, casts::CAST_POSSIBLE_TRUNCATION, casts::CAST_POSSIBLE_WRAP, casts::CAST_PRECISION_LOSS, @@ -162,6 +164,7 @@ store.register_lints(&[ format_args::FORMAT_IN_FORMAT_ARGS, format_args::TO_STRING_IN_FORMAT_ARGS, format_args::UNINLINED_FORMAT_ARGS, + format_args::UNUSED_FORMAT_SPECS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, format_push_string::FORMAT_PUSH_STRING, @@ -258,6 +261,7 @@ store.register_lints(&[ match_result_ok::MATCH_RESULT_OK, matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MANUAL_FILTER, matches::MANUAL_MAP, matches::MANUAL_UNWRAP_OR, matches::MATCH_AS_REF, @@ -403,6 +407,7 @@ store.register_lints(&[ missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + missing_trait_methods::MISSING_TRAIT_METHODS, mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION, mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, module_style::MOD_MODULE_FILES, @@ -475,6 +480,7 @@ store.register_lints(&[ panic_unimplemented::TODO, panic_unimplemented::UNIMPLEMENTED, panic_unimplemented::UNREACHABLE, + partial_pub_fields::PARTIAL_PUB_FIELDS, partialeq_ne_impl::PARTIALEQ_NE_IMPL, partialeq_to_none::PARTIALEQ_TO_NONE, pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index e0b4639af53e..65616d28d8f1 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -4,6 +4,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), + LintId::of(casts::AS_PTR_CAST_MUT), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index bc2f0beb358a..060d3bdc7c6f 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -29,12 +29,10 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(functions::MUST_USE_CANDIDATE), LintId::of(functions::TOO_MANY_LINES), LintId::of(if_not_else::IF_NOT_ELSE), LintId::of(implicit_hasher::IMPLICIT_HASHER), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(infinite_iter::MAYBE_INFINITE_ITER), LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 6eb9b3d3b9b7..f62d57af5b47 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -47,6 +47,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), + LintId::of(missing_trait_methods::MISSING_TRAIT_METHODS), LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION), LintId::of(module_style::MOD_MODULE_FILES), LintId::of(module_style::SELF_NAMED_MODULE_FILES), @@ -61,6 +62,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(panic_unimplemented::TODO), LintId::of(panic_unimplemented::UNIMPLEMENTED), LintId::of(panic_unimplemented::UNREACHABLE), + LintId::of(partial_pub_fields::PARTIAL_PUB_FIELDS), LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), LintId::of(pub_use::PUB_USE), LintId::of(redundant_slicing::DEREF_BY_SLICING), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 8e1390167dc8..6894d69e928a 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -25,12 +25,14 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(enum_variants::MODULE_INCEPTION), LintId::of(eta_reduction::REDUNDANT_CLOSURE), LintId::of(float_literal::EXCESSIVE_PRECISION), + LintId::of(format_args::UNINLINED_FORMAT_ARGS), LintId::of(from_over_into::FROM_OVER_INTO), LintId::of(from_str_radix_10::FROM_STR_RADIX_10), LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), + LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index d6d95c95c85d..b70c4bb73e57 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -11,6 +11,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), + LintId::of(casts::CAST_NAN_TO_INT), LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(drop_forget_ref::DROP_NON_DROP), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 89ffca8128a9..1307096b28d7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -39,7 +39,6 @@ extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; -extern crate rustc_mir_dataflow; extern crate rustc_parse; extern crate rustc_session; extern crate rustc_span; @@ -291,6 +290,7 @@ mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; mod missing_inline; +mod missing_trait_methods; mod mixed_read_write_in_expression; mod module_style; mod multi_assignments; @@ -326,6 +326,7 @@ mod option_if_let_else; mod overflow_check_conditional; mod panic_in_result_fn; mod panic_unimplemented; +mod partial_pub_fields; mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; @@ -419,7 +420,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None @@ -435,7 +436,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { .and_then(|v| parse_msrv(&v, None, None)); let clippy_msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file. `{s}` is not a valid Rust version" )); None @@ -446,7 +447,7 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { if let Some(clippy_msrv) = clippy_msrv { // if both files have an msrv, let's compare them and emit a warning if they differ if clippy_msrv != cargo_msrv { - sess.warn(&format!( + sess.warn(format!( "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" )); } @@ -475,7 +476,7 @@ pub fn read_conf(sess: &Session) -> Conf { let TryConf { conf, errors, warnings } = utils::conf::read(&file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { - sess.err(&format!( + sess.err(format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), format_error(error) @@ -483,7 +484,7 @@ pub fn read_conf(sess: &Session) -> Conf { } for warning in warnings { - sess.struct_warn(&format!( + sess.struct_warn(format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), format_error(warning) @@ -530,17 +531,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // all the internal lints #[cfg(feature = "internal")] { - store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); - store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); - store.register_late_pass(|_| Box::new(utils::internal_lints::CollapsibleCalls)); - store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new())); - store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle)); - store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath)); - store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl)); + store.register_early_pass(|| Box::new(utils::internal_lints::clippy_lints_internal::ClippyLintsInternal)); + store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce)); + store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls)); + store.register_late_pass(|_| { + Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new()) + }); + store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle)); + store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); + store.register_late_pass(|_| { + Box::::default() + }); + store.register_late_pass(|_| { + Box::::default() + }); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass)); + store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl)); } let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone(); @@ -910,6 +917,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)); store.register_late_pass(|_| Box::new(box_default::BoxDefault)); store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); + store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields)); + store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 00cfc6d49f19..27ba27202bf7 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; +use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -263,7 +263,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { match res { Res::Local(hir_id) => { let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let extent = self.cx + let extent = self + .cx .tcx .region_scope_tree(parent_def_id) .var_scope(hir_id.local_id) @@ -274,11 +275,12 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), ); } else { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); + self.indexed_indirectly + .insert(seqvar.segments[0].ident.name, Some(extent)); } - return false; // no need to walk further *on the variable* - } - Res::Def(DefKind::Static (_)| DefKind::Const, ..) => { + return false; // no need to walk further *on the variable* + }, + Res::Def(DefKind::Static(_) | DefKind::Const, ..) => { if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, @@ -287,8 +289,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } else { self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); } - return false; // no need to walk further *on the variable* - } + return false; // no need to walk further *on the variable* + }, _ => (), } } @@ -302,17 +304,26 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if_chain! { // a range index op if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind; - if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) - || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); + if let Some(trait_id) = self + .cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .and_then(|def_id| self.cx.tcx.trait_of_item(def_id)); + if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) + || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)); if !self.check(args_1, args_0, expr); - then { return } + then { + return; + } } if_chain! { // an index op if let ExprKind::Index(seqexpr, idx) = expr.kind; if !self.check(idx, seqexpr, expr); - then { return } + then { + return; + } } if_chain! { diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 153f97e4e66c..55989f8a4465 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -331,9 +331,8 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & } if let Some(e) = get_enclosing_loop_or_multi_call_closure(cx, loop_expr) { - let local_id = match iter_expr.path { - Res::Local(id) => id, - _ => return true, + let Res::Local(local_id) = iter_expr.path else { + return true }; let mut v = NestedLoopVisitor { cx, diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 9a0a26c0991b..090f9f8ff73c 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_function_call; -use clippy_utils::paths::FUTURE_FROM_GENERATOR; +use clippy_utils::match_function_call_with_def_id; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -140,9 +139,9 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t if args.bindings.len() == 1; let binding = &args.bindings[0]; if binding.ident.name == sym::Output; - if let TypeBindingKind::Equality{term: Term::Ty(output)} = binding.kind; + if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind; then { - return Some(output) + return Some(output); } } @@ -175,9 +174,16 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { if_chain! { if let Some(block_expr) = block.expr; - if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR); + if let Some(args) = cx + .tcx + .lang_items() + .from_generator_fn() + .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id)); if args.len() == 1; - if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0]; + if let Expr { + kind: ExprKind::Closure(&Closure { body, .. }), + .. + } = args[0]; let closure_body = cx.tcx.hir().body(body); if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block)); then { diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index ece4df95505c..02dc8755dd61 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -12,9 +12,9 @@ use std::ops::Deref; use clippy_utils::{ diagnostics::{span_lint_and_then, span_lint_hir_and_then}, - eq_expr_value, get_trait_def_id, + eq_expr_value, higher::If, - is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, paths, peel_blocks, + is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, sugg::Sugg, ty::implements_trait, @@ -190,7 +190,11 @@ impl TypeClampability { fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option { if ty.is_floating_point() { Some(TypeClampability::Float) - } else if get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) { + } else if cx + .tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { Some(TypeClampability::Ord) } else { None diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index fd14d868df34..33a052c41a38 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, @@ -63,7 +64,8 @@ fn check_arm<'tcx>( if !pat_contains_or(inner_then_pat); // the binding must come from the pattern of the containing match arm // .... => match { .. } - if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); + if let (Some(binding_span), is_innermost_parent_pat_struct) + = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id); // the "else" branches must be equal if match (outer_else_body, inner_else_body) { (None, None) => true, @@ -88,6 +90,13 @@ fn check_arm<'tcx>( if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, if outer_is_match { "match" } else { "if let" }, ); + // collapsing patterns need an explicit field name in struct pattern matching + // ex: Struct {x: Some(1)} + let replace_msg = if is_innermost_parent_pat_struct { + format!(", prefixed by {}:", snippet(cx, binding_span, "their field name")) + } else { + String::new() + }; span_lint_and_then( cx, COLLAPSIBLE_MATCH, @@ -96,7 +105,7 @@ fn check_arm<'tcx>( |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); - help_span.push_span_label(inner_then_pat.span, "with this pattern"); + help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); @@ -117,8 +126,9 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } } -fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { +fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option, bool) { let mut span = None; + let mut is_innermost_parent_pat_struct = false; pat.walk_short(|p| match &p.kind { // ignore OR patterns PatKind::Or(_) => false, @@ -129,9 +139,12 @@ fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { } !found }, - _ => true, + _ => { + is_innermost_parent_pat_struct = matches!(p.kind, PatKind::Struct(..)); + true + }, }); - span + (span, is_innermost_parent_pat_struct) } fn pat_contains_or(pat: &Pat<'_>) -> bool { diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs new file mode 100644 index 000000000000..66ba1f6f9c55 --- /dev/null +++ b/clippy_lints/src/matches/manual_filter.rs @@ -0,0 +1,153 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::contains_unsafe_block; +use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; + +use rustc_hir::LangItem::OptionSome; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +use super::manual_utils::{check_with, SomeExpr}; +use super::MANUAL_FILTER; + +// Function called on the of `[&+]Some((ref | ref mut) x) => ` +// Need to check if it's of the form `=if {} else {}` +// AND that only one `then/else_expr` resolves to `Some(x)` while the other resolves to `None` +// return the `cond` expression if so. +fn get_cond_expr<'tcx>( + cx: &LateContext<'tcx>, + pat: &Pat<'_>, + expr: &'tcx Expr<'_>, + ctxt: SyntaxContext, +) -> Option> { + if_chain! { + if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); + if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; + if let PatKind::Binding(_,target, ..) = pat.kind; + if let (then_visitor, else_visitor) + = (is_some_expr(cx, target, ctxt, then_expr), + is_some_expr(cx, target, ctxt, else_expr)); + if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` + then { + return Some(SomeExpr { + expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), + needs_unsafe_block: contains_unsafe_block(cx, expr), + needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond + }) + } + }; + None +} + +fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { + // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's + // checked by `contains_unsafe_block` + if let ExprKind::Block(block, None) = expr.kind { + if block.stmts.is_empty() { + return block.expr; + } + }; + None +} + +fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { + peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr) +} + +// function called for each expression: +// Some(x) => if { +// +// } else { +// +// } +// Returns true if resolves to `Some(x)`, `false` otherwise +fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { + // there can be not statements in the block as they would be removed when switching to `.filter` + if let ExprKind::Call(callee, [arg]) = inner_expr.kind { + return ctxt == expr.span.ctxt() + && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) + && path_to_local_id(arg, target); + } + }; + false +} + +// given the closure: `|| ` +// returns `|&| ` +fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String { + if has_copy_trait { + let mut with_ampersand = body_str; + with_ampersand.insert(1, '&'); + with_ampersand + } else { + body_str + } +} + +pub(super) fn check_match<'tcx>( + cx: &LateContext<'tcx>, + scrutinee: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], + expr: &'tcx Expr<'_>, +) { + let ty = cx.typeck_results().expr_ty(expr); + if is_type_diagnostic_item(cx, ty, sym::Option) + && let [first_arm, second_arm] = arms + && first_arm.guard.is_none() + && second_arm.guard.is_none() + { + check(cx, expr, scrutinee, first_arm.pat, first_arm.body, Some(second_arm.pat), second_arm.body); + } +} + +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, +) { + if let Some(sugg_info) = check_with( + cx, + expr, + scrutinee, + then_pat, + then_body, + else_pat, + else_body, + get_cond_expr, + ) { + let body_str = add_ampersand_if_copy(sugg_info.body_str, sugg_info.scrutinee_impl_copy); + span_lint_and_sugg( + cx, + MANUAL_FILTER, + expr.span, + "manual implementation of `Option::filter`", + "try this", + if sugg_info.needs_brackets { + format!( + "{{ {}{}.filter({body_str}) }}", + sugg_info.scrutinee_str, sugg_info.as_ref_str + ) + } else { + format!("{}{}.filter({body_str})", sugg_info.scrutinee_str, sugg_info.as_ref_str) + }, + sugg_info.app, + ); + } +} diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs index 76f5e1c941c7..aaba239677ff 100644 --- a/clippy_lints/src/matches/manual_map.rs +++ b/clippy_lints/src/matches/manual_map.rs @@ -1,22 +1,13 @@ -use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use super::manual_utils::{check_with, SomeExpr}; +use super::MANUAL_MAP; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; -use clippy_utils::{ - can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, -}; -use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{ - def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, - QPath, UnsafeSource, -}; -use rustc_lint::LateContext; -use rustc_span::{sym, SyntaxContext}; -use super::MANUAL_MAP; +use clippy_utils::{is_res_lang_ctor, path_res}; + +use rustc_hir::LangItem::OptionSome; +use rustc_hir::{Arm, Block, BlockCheckMode, Expr, ExprKind, Pat, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_span::SyntaxContext; pub(super) fn check_match<'tcx>( cx: &LateContext<'tcx>, @@ -43,7 +34,6 @@ pub(super) fn check_if_let<'tcx>( check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); } -#[expect(clippy::too_many_lines)] fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -53,254 +43,74 @@ fn check<'tcx>( else_pat: Option<&'tcx Pat<'_>>, else_body: &'tcx Expr<'_>, ) { - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) - { - return; - } - - let expr_ctxt = expr.span.ctxt(); - let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( - try_parse_pattern(cx, then_pat, expr_ctxt), - else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + if let Some(sugg_info) = check_with( + cx, + expr, + scrutinee, + then_pat, + then_body, + else_pat, + else_body, + get_some_expr, ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, false) - }, - _ => return, - }; - - // Top level or patterns aren't allowed in closures. - if matches!(some_pat.kind, PatKind::Or(_)) { - return; - } - - let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) { - Some(expr) => expr, - None => return, - }; - - // These two lints will go back and forth with each other. - if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit - && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) - { - return; - } - - // `map` won't perform any adjustments. - if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { - return; - } - - // Determine which binding mode to use. - let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability)); - - let as_ref_str = match binding_ref { - Some(Mutability::Mut) => ".as_mut()", - Some(Mutability::Not) => ".as_ref()", - None => "", - }; - - match can_move_expr_to_closure(cx, some_expr.expr) { - Some(captures) => { - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if let Some(binding_ref_mutability) = binding_ref { - let e = peel_hir_expr_while(scrutinee, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, - Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return; - }, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } - } - } - }, - None => return, - }; - - let mut app = Applicability::MachineApplicable; - - // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or - // it's being passed by value. - let scrutinee = peel_hir_expr_refs(scrutinee).0; - let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({scrutinee_str})") - } else { - scrutinee_str.into() - }; - - let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - if_chain! { - if !some_expr.needs_unsafe_block; - if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); - if func.span.ctxt() == some_expr.expr.span.ctxt(); - then { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + if sugg_info.needs_brackets { + format!( + "{{ {}{}.map({}) }}", + sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str + ) } else { - if path_to_local_id(some_expr.expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return; - } - - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::MUT) { - "mut " - } else { - "" - }; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{annotation}{some_binding}| unsafe {{ {expr_snip} }}") - } else { - format!("|{annotation}{some_binding}| {expr_snip}") - } - } - } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. - let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{pat_snip}| unsafe {{ {expr_snip} }}") - } else { - format!("|{pat_snip}| {expr_snip}") - } - } else { - // Refutable bindings and mixed reference annotations can't be handled by `map`. - return; - }; - - span_lint_and_sugg( - cx, - MANUAL_MAP, - expr.span, - "manual implementation of `Option::map`", - "try this", - if else_pat.is_none() && is_else_clause(cx.tcx, expr) { - format!("{{ {scrutinee_str}{as_ref_str}.map({body_str}) }}") - } else { - format!("{scrutinee_str}{as_ref_str}.map({body_str})") - }, - app, - ); -} - -// Checks whether the expression could be passed as a function, or whether a closure is needed. -// Returns the function to be passed to `map` if it exists. -fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - match expr.kind { - ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) - && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => - { - Some(func) - }, - _ => None, - } -} - -enum OptionPat<'a> { - Wild, - None, - Some { - // The pattern contained in the `Some` tuple. - pattern: &'a Pat<'a>, - // The number of references before the `Some` tuple. - // e.g. `&&Some(_)` has a ref count of 2. - ref_count: usize, - }, -} - -struct SomeExpr<'tcx> { - expr: &'tcx Expr<'tcx>, - needs_unsafe_block: bool, -} - -// Try to parse into a recognized `Option` pattern. -// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { - fn f<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - ref_count: usize, - ctxt: SyntaxContext, - ) -> Option> { - match pat.kind { - PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { - Some(OptionPat::None) - }, - PatKind::TupleStruct(ref qpath, [pattern], _) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => - { - Some(OptionPat::Some { pattern, ref_count }) + format!( + "{}{}.map({})", + sugg_info.scrutinee_str, sugg_info.as_ref_str, sugg_info.body_str + ) }, - _ => None, - } + sugg_info.app, + ); } - f(cx, pat, 0, ctxt) } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, + _: &'tcx Pat<'_>, expr: &'tcx Expr<'_>, - needs_unsafe_block: bool, ctxt: SyntaxContext, ) -> Option> { - // TODO: Allow more complex expressions. - match expr.kind { - ExprKind::Call(callee, [arg]) - if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => - { - Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }) - }, - ExprKind::Block( - Block { - stmts: [], - expr: Some(expr), - rules, - .. + fn get_some_expr_internal<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + needs_unsafe_block: bool, + ctxt: SyntaxContext, + ) -> Option> { + // TODO: Allow more complex expressions. + match expr.kind { + ExprKind::Call(callee, [arg]) + if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) => + { + Some(SomeExpr::new_no_negated(arg, needs_unsafe_block)) }, - _, - ) => get_some_expr( - cx, - expr, - needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), - ctxt, - ), - _ => None, + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + rules, + .. + }, + _, + ) => get_some_expr_internal( + cx, + expr, + needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + ctxt, + ), + _ => None, + } } -} - -// Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) + get_some_expr_internal(cx, expr, false, ctxt) } diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs new file mode 100644 index 000000000000..5b7644a53832 --- /dev/null +++ b/clippy_lints/src/matches/manual_utils.rs @@ -0,0 +1,277 @@ +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::{ + can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, + peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, sugg::Sugg, CaptureKind, +}; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_lines)] +pub(super) fn check_with<'tcx, F>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, + get_some_expr_fn: F, +) -> Option> +where + F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, +{ + let (scrutinee_ty, ty_ref_count, ty_mutability) = + peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + { + return None; + } + + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, then_pat, expr_ctxt), + else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, false) + }, + _ => return None, + }; + + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return None; + } + + let Some(some_expr) = get_some_expr_fn(cx, some_pat, some_expr, expr_ctxt) else { + return None; + }; + + // These two lints will go back and forth with each other. + if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit + && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return None; + } + + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { + return None; + } + + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability)); + + let as_ref_str = match binding_ref { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; + + match can_move_expr_to_closure(cx, some_expr.expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return None; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } + }, + None => return None, + }; + + let mut app = Applicability::MachineApplicable; + + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. + let scrutinee = peel_hir_expr_refs(scrutinee).0; + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({scrutinee_str})") + } else { + scrutinee_str.into() + }; + + let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app); + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + if_chain! { + if !some_expr.needs_unsafe_block; + if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); + if func.span.ctxt() == some_expr.expr.span.ctxt(); + then { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + if path_to_local_id(some_expr.expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return None; + } + + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::MUT) { + "mut " + } else { + "" + }; + + if some_expr.needs_unsafe_block { + format!("|{annotation}{some_binding}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{annotation}{some_binding}| {closure_expr_snip}") + } + } + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{pat_snip}| unsafe {{ {closure_expr_snip} }}") + } else { + format!("|{pat_snip}| {closure_expr_snip}") + } + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return None; + }; + + // relies on the fact that Option: Copy where T: copy + let scrutinee_impl_copy = is_copy(cx, scrutinee_ty); + + Some(SuggInfo { + needs_brackets: else_pat.is_none() && is_else_clause(cx.tcx, expr), + scrutinee_impl_copy, + scrutinee_str, + as_ref_str, + body_str, + app, + }) +} + +pub struct SuggInfo<'a> { + pub needs_brackets: bool, + pub scrutinee_impl_copy: bool, + pub scrutinee_str: String, + pub as_ref_str: &'a str, + pub body_str: String, + pub app: Applicability, +} + +// Checks whether the expression could be passed as a function, or whether a closure is needed. +// Returns the function to be passed to `map` if it exists. +fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + match expr.kind { + ExprKind::Call(func, [arg]) + if path_to_local_id(arg, binding) + && cx.typeck_results().expr_adjustments(arg).is_empty() + && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + { + Some(func) + }, + _ => None, + } +} + +#[derive(Debug)] +pub(super) enum OptionPat<'a> { + Wild, + None, + Some { + // The pattern contained in the `Some` tuple. + pattern: &'a Pat<'a>, + // The number of references before the `Some` tuple. + // e.g. `&&Some(_)` has a ref count of 2. + ref_count: usize, + }, +} + +pub(super) struct SomeExpr<'tcx> { + pub expr: &'tcx Expr<'tcx>, + pub needs_unsafe_block: bool, + pub needs_negated: bool, // for `manual_filter` lint +} + +impl<'tcx> SomeExpr<'tcx> { + pub fn new_no_negated(expr: &'tcx Expr<'tcx>, needs_unsafe_block: bool) -> Self { + Self { + expr, + needs_unsafe_block, + needs_negated: false, + } + } + + pub fn to_snippet_with_context( + &self, + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + app: &mut Applicability, + ) -> Sugg<'tcx> { + let sugg = Sugg::hir_with_context(cx, self.expr, ctxt, "..", app); + if self.needs_negated { !sugg } else { sugg } + } +} + +// Try to parse into a recognized `Option` pattern. +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. +pub(super) fn try_parse_pattern<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ctxt: SyntaxContext, +) -> Option> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option> { + match pat.kind { + PatKind::Wild => Some(OptionPat::Wild), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), + PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { + Some(OptionPat::None) + }, + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => + { + Some(OptionPat::Some { pattern, ref_count }) + }, + _ => None, + } + } + f(cx, pat, 0, ctxt) +} + +// Checks for the `None` value. +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) +} diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 37049f835775..168c1e4d2e60 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -221,7 +221,6 @@ fn iter_matching_struct_fields<'a>( #[expect(clippy::similar_names)] impl<'a> NormalizedPat<'a> { - #[expect(clippy::too_many_lines)] fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, @@ -235,9 +234,8 @@ impl<'a> NormalizedPat<'a> { Self::Struct(cx.qpath_res(path, pat.hir_id).opt_def_id(), fields) }, PatKind::TupleStruct(ref path, pats, wild_idx) => { - let adt = match cx.typeck_results().pat_ty(pat).ty_adt_def() { - Some(x) => x, - None => return Self::Wild, + let Some(adt) = cx.typeck_results().pat_ty(pat).ty_adt_def() else { + return Self::Wild }; let (var_id, variant) = if adt.is_enum() { match cx.qpath_res(path, pat.hir_id).opt_def_id() { diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 68682cedf1de..1bf8d4e96ad4 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -58,6 +58,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, Some(span), + true, ); span_lint_and_sugg( @@ -90,6 +91,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e &snippet_body, &mut applicability, None, + true, ); (expr.span, sugg) }, @@ -107,10 +109,14 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e }, PatKind::Wild => { if ex.can_have_side_effects() { - let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); - let sugg = format!( - "{};\n{indent}{snippet_body}", - snippet_with_applicability(cx, ex.span, "..", &mut applicability) + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &snippet_body, + &mut applicability, + None, + false, ); span_lint_and_sugg( @@ -169,6 +175,7 @@ fn sugg_with_curlies<'a>( snippet_body: &str, applicability: &mut Applicability, assignment: Option, + needs_var_binding: bool, ) -> String { let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); @@ -200,9 +207,15 @@ fn sugg_with_curlies<'a>( s }); - format!( - "{cbrace_start}let {} = {};\n{indent}{assignment_str}{snippet_body}{cbrace_end}", - snippet_with_applicability(cx, bind_names, "..", applicability), - snippet_with_applicability(cx, matched_vars, "..", applicability) - ) + let scrutinee = if needs_var_binding { + format!( + "let {} = {}", + snippet_with_applicability(cx, bind_names, "..", applicability), + snippet_with_applicability(cx, matched_vars, "..", applicability) + ) + } else { + snippet_with_applicability(cx, matched_vars, "..", applicability).to_string() + }; + + format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index e6b183fc05f2..7d8171ead89e 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,7 +1,9 @@ mod collapsible_match; mod infallible_destructuring_match; +mod manual_filter; mod manual_map; mod manual_unwrap_or; +mod manual_utils; mod match_as_ref; mod match_bool; mod match_like_matches; @@ -898,6 +900,34 @@ declare_clippy_lint! { "reimplementation of `map`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `match` which could be implemented using `filter` + /// + /// ### Why is this bad? + /// Using the `filter` method is clearer and more concise. + /// + /// ### Example + /// ```rust + /// match Some(0) { + /// Some(x) => if x % 2 == 0 { + /// Some(x) + /// } else { + /// None + /// }, + /// None => None, + /// }; + /// ``` + /// Use instead: + /// ```rust + /// Some(0).filter(|&x| x % 2 == 0); + /// ``` + #[clippy::version = "1.66.0"] + pub MANUAL_FILTER, + complexity, + "reimplentation of `filter`" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -939,6 +969,7 @@ impl_lint_pass!(Matches => [ SIGNIFICANT_DROP_IN_SCRUTINEE, TRY_ERR, MANUAL_MAP, + MANUAL_FILTER, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -988,6 +1019,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !in_constant(cx, expr.hir_id) { manual_unwrap_or::check(cx, expr, ex, arms); manual_map::check_match(cx, expr, ex, arms); + manual_filter::check_match(cx, ex, arms, expr); } if self.infallible_destructuring_match_linted { @@ -1014,6 +1046,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if !in_constant(cx, expr.hir_id) { manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr); + manual_filter::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); } } redundant_pattern_match::check_if_let( diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index d496107ffd6b..e5a15b2e1a1d 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{expr_block, snippet}; -use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs}; -use clippy_utils::{ - is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, -}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs}; +use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; use core::cmp::max; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -156,10 +155,10 @@ fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> /// Returns `true` if the given type is an enum we know won't be expanded in the future fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { // list of candidate `Enum`s we know will never get any more members - let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT]; + let candidates = [sym::Cow, sym::Option, sym::Result]; for candidate_ty in candidates { - if match_type(cx, ty, candidate_ty) { + if is_type_diagnostic_item(cx, ty, candidate_ty) { return true; } } diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index be56b63506a4..8adf9e370592 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -42,9 +42,8 @@ pub(super) fn check( fn ty_has_iter_method(cx: &LateContext<'_>, self_ref_ty: Ty<'_>) -> Option<(Symbol, &'static str)> { has_iter_method(cx, self_ref_ty).map(|ty_name| { - let mutbl = match self_ref_ty.kind() { - ty::Ref(_, _, mutbl) => mutbl, - _ => unreachable!(), + let ty::Ref(_, _, mutbl) = self_ref_ty.kind() else { + unreachable!() }; let method_name = match mutbl { hir::Mutability::Not => "iter", diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index ec694cf6882e..b80541b86479 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -21,11 +21,7 @@ pub fn check( return; } - let mm = if let Some(mm) = is_min_or_max(cx, unwrap_arg) { - mm - } else { - return; - }; + let Some(mm) = is_min_or_max(cx, unwrap_arg) else { return }; if ty.is_signed() { use self::{ @@ -33,9 +29,7 @@ pub fn check( Sign::{Neg, Pos}, }; - let sign = if let Some(sign) = lit_sign(arith_rhs) { - sign - } else { + let Some(sign) = lit_sign(arith_rhs) else { return; }; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index cfcf9596c50d..fb92779be2a7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -102,9 +102,7 @@ use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{ - contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty, -}; +use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; @@ -3372,7 +3370,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { let first_arg_span = first_arg_ty.span; let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); + let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()) + .self_ty() + .skip_binder(); wrong_self_convention::check( cx, item.ident.name.as_str(), @@ -3380,7 +3380,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { first_arg_ty, first_arg_span, false, - true + true, ); } } @@ -3389,7 +3389,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id()); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); + let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()) + .self_ty() + .skip_binder(); if !ret_ty.contains(self_ty); then { @@ -3846,14 +3848,13 @@ impl SelfKind { return m == mutability && t == parent_ty; } - let trait_path = match mutability { - hir::Mutability::Not => &paths::ASREF_TRAIT, - hir::Mutability::Mut => &paths::ASMUT_TRAIT, + let trait_sym = match mutability { + hir::Mutability::Not => sym::AsRef, + hir::Mutability::Mut => sym::AsMut, }; - let trait_def_id = match get_trait_def_id(cx, trait_path) { - Some(did) => did, - None => return false, + let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { + return false }; implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) } diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 6fb92d1c663c..742483e6b2e5 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -32,8 +32,7 @@ pub(super) fn check<'tcx>( return; } - let deref_aliases: [&[&str]; 9] = [ - &paths::DEREF_TRAIT_METHOD, + let deref_aliases: [&[&str]; 8] = [ &paths::DEREF_MUT_TRAIT_METHOD, &paths::CSTRING_AS_C_STR, &paths::OS_STRING_AS_OS_STR, @@ -45,12 +44,14 @@ pub(super) fn check<'tcx>( ]; let is_deref = match map_arg.kind { - hir::ExprKind::Path(ref expr_qpath) => cx - .qpath_res(expr_qpath, map_arg.hir_id) - .opt_def_id() - .map_or(false, |fun_def_id| { - deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) - }), + hir::ExprKind::Path(ref expr_qpath) => { + cx.qpath_res(expr_qpath, map_arg.hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) + || deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }) + }, hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(closure_body.value); @@ -68,7 +69,8 @@ pub(super) fn check<'tcx>( if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; then { let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + cx.tcx.is_diagnostic_item(sym::deref_method, method_did) + || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) } else { false } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 6a35024d0361..991d3dd538bf 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; -use clippy_utils::ty::{implements_trait, match_type}; -use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{contains_return, is_trait_item, last_path_segment}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Symbol}; use std::borrow::Cow; use super::OR_FUN_CALL; @@ -88,11 +88,11 @@ pub(super) fn check<'tcx>( fun_span: Option, ) { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ - (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (&paths::RESULT, true, &["or", "unwrap_or"], "else"), + const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ + (sym::BTreeEntry, false, &["or_insert"], "with"), + (sym::HashMapEntry, false, &["or_insert"], "with"), + (sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), + (sym::Result, true, &["or", "unwrap_or"], "else"), ]; if_chain! { @@ -104,7 +104,7 @@ pub(super) fn check<'tcx>( let self_ty = cx.typeck_results().expr_ty(self_expr); if let Some(&(_, fn_has_arguments, poss, suffix)) = - KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0)); + KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0)); if poss.contains(&name); @@ -121,10 +121,9 @@ pub(super) fn check<'tcx>( macro_expanded_snipped = snippet(cx, snippet_span, ".."); match macro_expanded_snipped.strip_prefix("$crate::vec::") { Some(stripped) => Cow::from(stripped), - None => macro_expanded_snipped + None => macro_expanded_snipped, } - } - else { + } else { not_macro_argument_snippet } }; diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index ae3594bd36c3..1acac59144ce 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -289,9 +289,7 @@ fn parse_iter_usage<'tcx>( ) -> Option { let (kind, span) = match iter.next() { Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { - let (name, args) = if let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind { - (name, args) - } else { + let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind else { return None; }; let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?; diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 5cf88bfc8880..3566fe9a0bbc 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -363,7 +363,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< && let output_ty = return_ty(cx, item.hir_id()) && let local_def_id = cx.tcx.hir().local_def_id(item.hir_id()) && Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.hir_id()); + let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.hir_id()); fn_ctxt.can_coerce(ty, output_ty) }) { if has_lifetime(output_ty) && has_lifetime(ty) { diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs index 62c6ca32d31a..27e7f8505eb5 100644 --- a/clippy_lints/src/misc_early/literal_suffix.rs +++ b/clippy_lints/src/misc_early/literal_suffix.rs @@ -6,9 +6,7 @@ use rustc_lint::EarlyContext; use super::{SEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX}; pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) { - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { + let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; // Do not lint when literal is unsuffixed. diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 80e242131007..263ee1e945a2 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -5,9 +5,7 @@ use rustc_lint::EarlyContext; use super::MIXED_CASE_HEX_LITERALS; pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) { - let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) { - val - } else { + let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; if maybe_last_sep_idx <= 2 { diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 4963bba82f2d..9ead43ea4a47 100644 --- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -6,6 +6,7 @@ use rustc_lint::EarlyContext; use super::ZERO_PREFIXED_LITERAL; pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { + let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0'); span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, @@ -15,15 +16,18 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { diag.span_suggestion( lit.span, "if you mean to use a decimal constant, remove the `0` to avoid confusion", - lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - lit.span, - "if you mean to use an octal constant, use `0o`", - format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), + trimmed_lit_snip.to_string(), Applicability::MaybeIncorrect, ); + // do not advise to use octal form if the literal cannot be expressed in base 8. + if !lit_snip.contains(|c| c == '8' || c == '9') { + diag.span_suggestion( + lit.span, + "if you mean to use an octal constant, use `0o`", + format!("0o{trimmed_lit_snip}"), + Applicability::MaybeIncorrect, + ); + } }, ); } diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 6dd76a6531e4..9de4b56b77b5 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -70,9 +70,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { // find the type that the Impl is for // only lint on struct/enum/union for now - let defid = match path.res { - Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid, - _ => return, + let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) = path.res else { + return }; // get the names of the generic parameters in the type diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs new file mode 100644 index 000000000000..68af8a672f6a --- /dev/null +++ b/clippy_lints/src/missing_trait_methods.rs @@ -0,0 +1,98 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_lint_allowed; +use clippy_utils::macros::span_is_local; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::AssocItem; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks if a provided method is used implicitly by a trait + /// implementation. A usage example would be a wrapper where every method + /// should perform some operation before delegating to the inner type's + /// implemenation. + /// + /// This lint should typically be enabled on a specific trait `impl` item + /// rather than globally. + /// + /// ### Why is this bad? + /// Indicates that a method is missing. + /// + /// ### Example + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// } + /// ``` + /// Use instead: + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// + /// fn provided() { /* ... */ } + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub MISSING_TRAIT_METHODS, + restriction, + "trait implementation uses default provided method" +} +declare_lint_pass!(MissingTraitMethods => [MISSING_TRAIT_METHODS]); + +impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id()) + && span_is_local(item.span) + && let ItemKind::Impl(Impl { + items, + of_trait: Some(trait_ref), + .. + }) = item.kind + && let Some(trait_id) = trait_ref.trait_def_id() + { + let mut provided: DefIdMap<&AssocItem> = cx + .tcx + .provided_trait_methods(trait_id) + .map(|assoc| (assoc.def_id, assoc)) + .collect(); + + for impl_item in *items { + if let Some(def_id) = impl_item.trait_item_def_id { + provided.remove(&def_id); + } + } + + for assoc in provided.values() { + let source_map = cx.tcx.sess.source_map(); + let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); + + span_lint_and_help( + cx, + MISSING_TRAIT_METHODS, + source_map.guess_head_span(item.span), + &format!("missing trait method provided by default: `{}`", assoc.name), + Some(definition_span), + "implement the method", + ); + } + } + } +} diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index a2419c277e9c..6752976348f6 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -190,10 +190,7 @@ fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) { if parent_id == cur_id { break; } - let parent_node = match map.find(parent_id) { - Some(parent) => parent, - None => break, - }; + let Some(parent_node) = map.find(parent_id) else { break }; let stop_early = match parent_node { Node::Expr(expr) => check_expr(vis, expr), diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 3233d87c0731..c3b633fd6a03 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -49,9 +49,8 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - let expr = match stmt.kind { - StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, - _ => return, + let (StmtKind::Expr(expr) | StmtKind::Semi(expr)) = stmt.kind else { + return }; if_chain! { diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index a7e0e35787cf..5c2b96f5b2ce 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::implements_trait; -use clippy_utils::{self, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -47,18 +47,16 @@ declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]); impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if !in_external_macro(cx.sess(), expr.span); if let ExprKind::Unary(UnOp::Not, inner) = expr.kind; if let ExprKind::Binary(ref op, left, _) = inner.kind; if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node; then { - let ty = cx.typeck_results().expr_ty(left); let implements_ord = { - if let Some(id) = get_trait_def_id(cx, &paths::ORD) { + if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) { implements_trait(cx, ty, id, &[]) } else { return; @@ -81,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { "the use of negated comparison operators on partially ordered \ types produces code that is hard to read and refactor, please \ consider using the `partial_cmp` method instead, to make it \ - clear that the two values could be incomparable" + clear that the two values could be incomparable", ); } } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 2c839d029c6f..a6742824bc56 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -357,9 +357,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } // Make sure it is a const item. - let item_def_id = match cx.qpath_res(qpath, expr.hir_id) { - Res::Def(DefKind::Const | DefKind::AssocConst, did) => did, - _ => return, + let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else { + return }; // Climb up to resolve any field access and explicit referencing. diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 1a765b14892f..2ecb04874842 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -55,9 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { if let ExprKind::Lit(_) = param.kind; then { - let snip = match snippet_opt(cx, param.span) { - Some(s) => s, - _ => return, + let Some(snip) = snippet_opt(cx, param.span) else { + return }; if !snip.starts_with("0o") { @@ -72,16 +71,10 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE); if let ExprKind::Lit(_) = param.kind; - + if let Some(snip) = snippet_opt(cx, param.span); + if !snip.starts_with("0o"); then { - let snip = match snippet_opt(cx, param.span) { - Some(s) => s, - _ => return, - }; - - if !snip.starts_with("0o") { - show_error(cx, param); - } + show_error(cx, param); } } }, diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 0ca0befc1351..6c909e5ed73e 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -266,7 +266,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { .iter() .find(|b| b.0 == brace) .map(|(o, c)| ((*o).to_owned(), (*c).to_owned())) - .ok_or_else(|| de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, + .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?, }) } } diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index c9c777f1bd8d..24aeb82a37f3 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{match_any_def_paths, path_def_id, paths}; +use clippy_utils::{match_def_path, path_def_id, paths}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -49,13 +49,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path) - .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) - .map_or(false, |idx| match idx { - 0 => true, - 1 => !is_copy(cx, typeck.expr_ty(expr)), - _ => false, - }) => + if path_def_id(cx, path).map_or(false, |id| { + if match_def_path(cx, id, &paths::FROM_STR_METHOD) { + true + } else if cx.tcx.lang_items().from_fn() == Some(id) { + !is_copy(cx, typeck.expr_ty(expr)) + } else { + false + } + }) => { (arg, arg.span) }, diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs new file mode 100644 index 000000000000..f60d9d65b120 --- /dev/null +++ b/clippy_lints/src/partial_pub_fields.rs @@ -0,0 +1,81 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Item, ItemKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks whether partial fields of a struct are public. + /// + /// Either make all fields of a type public, or make none of them public + /// + /// ### Why is this bad? + /// Most types should either be: + /// * Abstract data types: complex objects with opaque implementation which guard + /// interior invariants and expose intentionally limited API to the outside world. + /// * Data: relatively simple objects which group a bunch of related attributes together. + /// + /// ### Example + /// ```rust + /// pub struct Color { + /// pub r: u8, + /// pub g: u8, + /// b: u8, + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub struct Color { + /// pub r: u8, + /// pub g: u8, + /// pub b: u8, + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub PARTIAL_PUB_FIELDS, + restriction, + "partial fields of a struct are public" +} +declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]); + +impl EarlyLintPass for PartialPubFields { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + let ItemKind::Struct(ref st, _) = item.kind else { + return; + }; + + let mut fields = st.fields().iter(); + let Some(first_field) = fields.next() else { + // Empty struct. + return; + }; + let all_pub = first_field.vis.kind.is_pub(); + let all_priv = !all_pub; + + let msg = "mixed usage of pub and non-pub fields"; + + for field in fields { + if all_priv && field.vis.kind.is_pub() { + span_lint_and_help( + cx, + PARTIAL_PUB_FIELDS, + field.vis.span, + msg, + None, + "consider using private field here", + ); + return; + } else if all_pub && !field.vis.kind.is_pub() { + span_lint_and_help( + cx, + PARTIAL_PUB_FIELDS, + field.vis.span, + msg, + None, + "consider using public field here", + ); + return; + } + } + } +} diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index d296a150b46d..40db315bf272 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -15,13 +15,17 @@ use rustc_hir::{ ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety, }; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Binder, ExistentialPredicate, List, PredicateKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::fmt; use std::iter; @@ -384,6 +388,17 @@ enum DerefTy<'tcx> { Slice(Option, Ty<'tcx>), } impl<'tcx> DerefTy<'tcx> { + fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> { + match *self { + Self::Str => cx.tcx.types.str_, + Self::Path => cx.tcx.mk_adt( + cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()), + List::empty(), + ), + Self::Slice(_, ty) => cx.tcx.mk_slice(ty), + } + } + fn argless_str(&self) -> &'static str { match *self { Self::Str => "str", @@ -552,9 +567,8 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } // Check if this is local we care about - let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) { - Some(&i) => i, - None => return walk_expr(self, e), + let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else { + return walk_expr(self, e); }; let args = &self.args[args_idx]; let result = &mut self.results[args_idx]; @@ -582,6 +596,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0); if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| { match *ty.skip_binder().peel_refs().kind() { + ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds), ty::Param(_) => true, ty::Adt(def, _) => def.did() == args.ty_did, _ => false, @@ -609,14 +624,15 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } } - let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) { - x - } else { + let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else { set_skip_flag(); return; }; match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() { + ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => { + set_skip_flag(); + }, ty::Param(_) => { set_skip_flag(); }, @@ -668,6 +684,30 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: v.results } +fn matches_preds<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + preds: &'tcx [Binder<'tcx, ExistentialPredicate<'tcx>>], +) -> bool { + let infcx = cx.tcx.infer_ctxt().build(); + preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) { + ExistentialPredicate::Trait(p) => infcx + .type_implements_trait(p.def_id, ty, p.substs, cx.param_env) + .must_apply_modulo_regions(), + ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new( + ObligationCause::dummy(), + cx.param_env, + cx.tcx.mk_predicate(Binder::bind_with_vars( + PredicateKind::Projection(p.with_self_ty(cx.tcx, ty)), + List::empty(), + )), + )), + ExistentialPredicate::AutoTrait(p) => infcx + .type_implements_trait(p, ty, List::empty(), cx.param_env) + .must_apply_modulo_regions(), + }) +} + fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { if let TyKind::Rptr(lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index b0a5d1a67582..72dda67c72b2 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -49,15 +49,13 @@ declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]); impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call - let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) { - Some(call_arg) => call_arg, - None => return, + let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else { + return }; // Check if the argument to the method call is a cast from usize - let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) { - Some(cast_lhs_expr) => cast_lhs_expr, - None => return, + let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else { + return }; let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 9fd86331ec75..aedbe08e3e46 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,25 +1,18 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{def_id, Body, FnDecl, HirId}; -use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::{ - self, traversal, - visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _}, - Mutability, -}; -use rustc_middle::ty::{self, visit::TypeVisitor, Ty}; -use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsCursor}; +use rustc_middle::mir; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::sym; -use std::ops::ControlFlow; macro_rules! unwrap_or_continue { ($x:expr) => { @@ -89,21 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let mir = cx.tcx.optimized_mir(def_id.to_def_id()); - let possible_origin = { - let mut vis = PossibleOriginVisitor::new(mir); - vis.visit_body(mir); - vis.into_map(cx) - }; - let maybe_storage_live_result = MaybeStorageLive - .into_engine(cx.tcx, mir) - .pass_name("redundant_clone") - .iterate_to_fixpoint() - .into_results_cursor(mir); - let mut possible_borrower = { - let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); - vis.visit_body(mir); - vis.into_map(cx, maybe_storage_live_result) - }; + let mut possible_borrower = PossibleBorrowerMap::new(cx, mir); for (bb, bbdata) in mir.basic_blocks.iter_enumerated() { let terminator = bbdata.terminator(); @@ -374,403 +353,40 @@ struct CloneUsage { /// Whether the clone value is mutated. clone_consumed_or_mutated: bool, } -fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { - struct V { - cloned: mir::Local, - clone: mir::Local, - result: CloneUsage, - } - impl<'tcx> mir::visit::Visitor<'tcx> for V { - fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { - let statements = &data.statements; - for (statement_index, statement) in statements.iter().enumerate() { - self.visit_statement(statement, mir::Location { block, statement_index }); - } - - self.visit_terminator( - data.terminator(), - mir::Location { - block, - statement_index: statements.len(), - }, - ); - } - - fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) { - let local = place.local; - - if local == self.cloned - && !matches!( - ctx, - PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) - ) - { - self.result.cloned_used = true; - self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| { - matches!( - ctx, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) - ) - .then(|| loc) - }); - } else if local == self.clone { - match ctx { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { - self.result.clone_consumed_or_mutated = true; - }, - _ => {}, - } - } - } - } - - let init = CloneUsage { - cloned_used: false, - cloned_consume_or_mutate_loc: None, - // Consider non-temporary clones consumed. - // TODO: Actually check for mutation of non-temporaries. - clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp, - }; - traversal::ReversePostorder::new(mir, bb) - .skip(1) - .fold(init, |usage, (tbb, tdata)| { - // Short-circuit - if (usage.cloned_used && usage.clone_consumed_or_mutated) || - // Give up on loops - tdata.terminator().successors().any(|s| s == bb) - { - return CloneUsage { - cloned_used: true, - clone_consumed_or_mutated: true, - ..usage - }; - } - - let mut v = V { - cloned, - clone, - result: usage, - }; - v.visit_basic_block_data(tbb, tdata); - v.result - }) -} - -/// Determines liveness of each local purely based on `StorageLive`/`Dead`. -#[derive(Copy, Clone)] -struct MaybeStorageLive; - -impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Domain = BitSet; - const NAME: &'static str = "maybe_storage_live"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = dead - BitSet::new_empty(body.local_decls.len()) - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { - for arg in body.args_iter() { - state.insert(arg); - } - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { - type Idx = mir::Local; - - fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { - match stmt.kind { - mir::StatementKind::StorageLive(l) => trans.gen(l), - mir::StatementKind::StorageDead(l) => trans.kill(l), - _ => (), - } - } - - fn terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _loc: mir::Location, - ) { - } - - fn call_return_effect( - &self, - _trans: &mut impl GenKill, - _block: mir::BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - // Nothing to do when a call returns successfully - } -} - -/// Collects the possible borrowers of each local. -/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` -/// possible borrowers of `a`. -struct PossibleBorrowerVisitor<'a, 'tcx> { - possible_borrower: TransitiveRelation, - body: &'a mir::Body<'tcx>, - cx: &'a LateContext<'tcx>, - possible_origin: FxHashMap>, -} - -impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> { - fn new( - cx: &'a LateContext<'tcx>, - body: &'a mir::Body<'tcx>, - possible_origin: FxHashMap>, - ) -> Self { - Self { - possible_borrower: TransitiveRelation::default(), - cx, - body, - possible_origin, - } - } - - fn into_map( - self, - cx: &LateContext<'tcx>, - maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>, - ) -> PossibleBorrowerMap<'a, 'tcx> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - - let bs = BitSet::new_empty(self.body.local_decls.len()); - PossibleBorrowerMap { - map, - maybe_live, - bitset: (bs.clone(), bs), - } - } -} - -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - mir::Rvalue::Ref(_, _, borrowed) => { - self.possible_borrower.add(borrowed.local, lhs); - }, - other => { - if ContainsRegion - .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) - .is_continue() - { - return; - } - rvalue_locals(other, |rhs| { - if lhs != rhs { - self.possible_borrower.add(rhs, lhs); - } - }); - }, - } - } - - fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { - if let mir::TerminatorKind::Call { - args, - destination: mir::Place { local: dest, .. }, - .. - } = &terminator.kind - { - // TODO add doc - // If the call returns something with lifetimes, - // let's conservatively assume the returned value contains lifetime of all the arguments. - // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. - - let mut immutable_borrowers = vec![]; - let mut mutable_borrowers = vec![]; - - for op in args { - match op { - mir::Operand::Copy(p) | mir::Operand::Move(p) => { - if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { - mutable_borrowers.push(p.local); - } else { - immutable_borrowers.push(p.local); - } - }, - mir::Operand::Constant(..) => (), - } - } - - let mut mutable_variables: Vec = mutable_borrowers - .iter() - .filter_map(|r| self.possible_origin.get(r)) - .flat_map(HybridBitSet::iter) - .collect(); - - if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { - mutable_variables.push(*dest); - } - - for y in mutable_variables { - for x in &immutable_borrowers { - self.possible_borrower.add(*x, y); - } - for x in &mutable_borrowers { - self.possible_borrower.add(*x, y); - } - } - } - } -} - -/// Collect possible borrowed for every `&mut` local. -/// For example, `_1 = &mut _2` generate _1: {_2,...} -/// Known Problems: not sure all borrowed are tracked -struct PossibleOriginVisitor<'a, 'tcx> { - possible_origin: TransitiveRelation, - body: &'a mir::Body<'tcx>, -} - -impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { - fn new(body: &'a mir::Body<'tcx>) -> Self { - Self { - possible_origin: TransitiveRelation::default(), - body, - } - } - - fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } - - let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); - if !borrowers.is_empty() { - map.insert(row, borrowers); - } - } - map - } -} - -impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - // Only consider `&mut`, which can modify origin place - mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | - // _2: &mut _; - // _3 = move _2 - mir::Rvalue::Use(mir::Operand::Move(borrowed)) | - // _3 = move _2 as &mut _; - mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _) - => { - self.possible_origin.add(lhs, borrowed.local); - }, - _ => {}, - } - } -} - -struct ContainsRegion; - -impl TypeVisitor<'_> for ContainsRegion { - type BreakTy = (); - - fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { - ControlFlow::BREAK - } -} - -fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { - use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; - - let mut visit_op = |op: &mir::Operand<'_>| match op { - mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), - mir::Operand::Constant(..) => (), - }; - match rvalue { - Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), - Aggregate(_, ops) => ops.iter().for_each(visit_op), - BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { - visit_op(lhs); - visit_op(rhs); +fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { + if let Some(( + LocalUsage { + local_use_locs: cloned_use_locs, + local_consume_or_mutate_locs: cloned_consume_or_mutate_locs, }, - _ => (), - } -} - -/// Result of `PossibleBorrowerVisitor`. -struct PossibleBorrowerMap<'a, 'tcx> { - /// Mapping `Local -> its possible borrowers` - map: FxHashMap>, - maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>, - // Caches to avoid allocation of `BitSet` on every query - bitset: (BitSet, BitSet), -} - -impl PossibleBorrowerMap<'_, '_> { - /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. - fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after_primary_effect(at); - - self.bitset.0.clear(); - let maybe_live = &mut self.maybe_live; - if let Some(bitset) = self.map.get(&borrowed) { - for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { - self.bitset.0.insert(b); - } - } else { - return false; - } - - self.bitset.1.clear(); - for b in borrowers { - self.bitset.1.insert(*b); + LocalUsage { + local_use_locs: _, + local_consume_or_mutate_locs: clone_consume_or_mutate_locs, + }, + )) = visit_local_usage( + &[cloned, clone], + mir, + mir::Location { + block: bb, + statement_index: mir.basic_blocks[bb].statements.len(), + }, + ) + .map(|mut vec| (vec.remove(0), vec.remove(0))) + { + CloneUsage { + cloned_used: !cloned_use_locs.is_empty(), + cloned_consume_or_mutate_loc: cloned_consume_or_mutate_locs.first().copied(), + // Consider non-temporary clones consumed. + // TODO: Actually check for mutation of non-temporaries. + clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp + || !clone_consume_or_mutate_locs.is_empty(), } - - self.bitset.0 == self.bitset.1 - } - - fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { - self.maybe_live.seek_after_primary_effect(at); - self.maybe_live.contains(local) - } -} - -#[derive(Default)] -struct TransitiveRelation { - relations: FxHashMap>, -} -impl TransitiveRelation { - fn add(&mut self, a: mir::Local, b: mir::Local) { - self.relations.entry(a).or_default().push(b); - } - - fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet { - let mut seen = HybridBitSet::new_empty(domain_size); - let mut stack = vec![a]; - while let Some(u) = stack.pop() { - if let Some(edges) = self.relations.get(&u) { - for &v in edges { - if seen.insert(v) { - stack.push(v); - } - } - } + } else { + CloneUsage { + cloned_used: true, + cloned_consume_or_mutate_loc: None, + clone_consumed_or_mutated: true, } - seen } } diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 42514f861be1..f21b3ea6c3b0 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -52,7 +52,8 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { GenericArg::Type(inner_ty) => Some(inner_ty), _ => None, }); - if let TyKind::Rptr(_, _) = inner_ty.kind; + if let TyKind::Rptr(_, ref inner_mut_ty) = inner_ty.kind; + if inner_mut_ty.mutbl == Mutability::Not; then { span_lint_and_sugg( diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 5dcdab5b8ab9..87f966ced0df 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -106,10 +106,7 @@ impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]); impl<'tcx> LateLintPass<'tcx> for Shadow { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - let (id, ident) = match pat.kind { - PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident), - _ => return, - }; + let PatKind::Binding(_, id, ident, _) = pat.kind else { return }; if pat.span.desugaring_kind().is_some() { return; diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 70d166c4854c..2f190e594a83 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -6,11 +6,7 @@ use rustc_span::DUMMY_SP; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment -pub(super) fn is_layout_incompatible<'tcx>( - cx: &LateContext<'tcx>, - from: Ty<'tcx>, - to: Ty<'tcx>, -) -> bool { +pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from) && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to) && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from)) @@ -33,9 +29,7 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, ) -> bool { - use CastKind::{ - AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast, - }; + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; matches!( check_cast(cx, e, from_ty, to_ty), Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) @@ -46,20 +40,18 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( /// the cast. In certain cases, including some invalid casts from array references /// to pointers, this may cause additional errors to be emitted and/or ICE error /// messages. This function will panic if that occurs. -fn check_cast<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, -) -> Option { +fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option { let hir_id = e.hir_id; let local_def_id = hir_id.owner.def_id; Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id); + let fn_ctxt = FnCtxt::new(inherited, cx.param_env, hir_id); // If we already have errors, we can't be sure we can pointer cast. - assert!(!fn_ctxt.errors_reported_since_creation(), "Newly created FnCtxt contained errors"); + assert!( + !fn_ctxt.errors_reported_since_creation(), + "Newly created FnCtxt contained errors" + ); if let Ok(check) = cast::CastCheck::new( &fn_ctxt, e, from_ty, to_ty, diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index 6b9de64e24c9..fa567b9b2d24 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -9,6 +9,7 @@ use rustc_span::symbol::sym; use super::RC_BUFFER; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + let app = Applicability::Unspecified; if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { if let Some(alternate) = match_buffer_type(cx, qpath) { span_lint_and_sugg( @@ -18,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "usage of `Rc` when T is a buffer type", "try", format!("Rc<{alternate}>"), - Applicability::MachineApplicable, + app, ); } else { let Some(ty) = qpath_generic_tys(qpath).next() else { return false }; @@ -26,15 +27,12 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ if !cx.tcx.is_diagnostic_item(sym::Vec, id) { return false; } - let qpath = match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return false, - }; + let TyKind::Path(qpath) = &ty.kind else { return false }; let inner_span = match qpath_generic_tys(qpath).next() { Some(ty) => ty.span, None => return false, }; - let mut applicability = Applicability::MachineApplicable; + let mut applicability = app; span_lint_and_sugg( cx, RC_BUFFER, @@ -45,7 +43,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "Rc<[{}]>", snippet_with_applicability(cx, inner_span, "..", &mut applicability) ), - Applicability::MachineApplicable, + app, ); return true; } @@ -58,22 +56,19 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "usage of `Arc` when T is a buffer type", "try", format!("Arc<{alternate}>"), - Applicability::MachineApplicable, + app, ); } else if let Some(ty) = qpath_generic_tys(qpath).next() { let Some(id) = path_def_id(cx, ty) else { return false }; if !cx.tcx.is_diagnostic_item(sym::Vec, id) { return false; } - let qpath = match &ty.kind { - TyKind::Path(qpath) => qpath, - _ => return false, - }; + let TyKind::Path(qpath) = &ty.kind else { return false }; let inner_span = match qpath_generic_tys(qpath).next() { Some(ty) => ty.span, None => return false, }; - let mut applicability = Applicability::MachineApplicable; + let mut applicability = app; span_lint_and_sugg( cx, RC_BUFFER, @@ -84,7 +79,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ "Arc<[{}]>", snippet_with_applicability(cx, inner_span, "..", &mut applicability) ), - Applicability::MachineApplicable, + app, ); return true; } diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index ecb672005390..7883353e3fef 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -10,6 +10,7 @@ use rustc_span::symbol::sym; use super::{utils, REDUNDANT_ALLOCATION}; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + let mut applicability = Applicability::MaybeIncorrect; let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { "Box" } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { @@ -21,7 +22,6 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; if let Some(span) = utils::match_borrows_parameter(cx, qpath) { - let mut applicability = Applicability::MaybeIncorrect; let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_then( cx, @@ -47,9 +47,8 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ _ => return false, }; - let inner_qpath = match &ty.kind { - TyKind::Path(inner_qpath) => inner_qpath, - _ => return false, + let TyKind::Path(inner_qpath) = &ty.kind else { + return false }; let inner_span = match qpath_generic_tys(inner_qpath).next() { Some(ty) => { @@ -64,7 +63,6 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ None => return false, }; if inner_sym == outer_sym { - let mut applicability = Applicability::MaybeIncorrect; let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability); span_lint_and_then( cx, diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 57aff5367dd1..1307288623f9 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::{get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; @@ -7,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{BytePos, Span}; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -80,7 +79,7 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve let fn_sig = cx.tcx.fn_sig(def_id); let generics = cx.tcx.predicates_of(def_id); let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait()); - let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD)); + let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord)); let partial_ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait()); // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error @@ -99,11 +98,15 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve if trait_pred.self_ty() == inp; if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); then { - if ord_preds.iter().any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) { + if ord_preds + .iter() + .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) + { args_to_check.push((i, "Ord".to_string())); - } else if partial_ord_preds.iter().any(|pord| { - pord.self_ty() == return_ty_pred.term.ty().unwrap() - }) { + } else if partial_ord_preds + .iter() + .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) + { args_to_check.push((i, "PartialOrd".to_string())); } } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index fb73c386640b..b305dae76084 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -163,9 +163,8 @@ fn unnest_or_patterns(pat: &mut P) -> bool { noop_visit_pat(p, self); // Don't have an or-pattern? Just quit early on. - let alternatives = match &mut p.kind { - Or(ps) => ps, - _ => return, + let Or(alternatives) = &mut p.kind else { + return }; // Collapse or-patterns directly nested in or-patterns. diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 8bcdff66331d..92053cec59fc 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -47,9 +47,8 @@ declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]); impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - let expr = match s.kind { - hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, - _ => return, + let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = s.kind else { + return }; match expr.kind { diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index a82643a59f97..1f69db1cbca4 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -55,9 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { match e.kind { ExprKind::Match(_, arms, MatchSource::TryDesugar) => { - let e = match arms[0].body.kind { - ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e)) => e, - _ => return, + let (ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e))) = arms[0].body.kind else { + return }; if let ExprKind::Call(_, [arg, ..]) = e.kind { self.try_desugar_arm.push(arg.hir_id); diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index e069de8cb5c7..0c052d86eda4 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -12,6 +12,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::{Ident, Symbol}; +use std::cell::Cell; use std::fmt::{Display, Formatter, Write as _}; declare_clippy_lint! { @@ -37,15 +38,13 @@ declare_clippy_lint! { /// /// ```rust,ignore /// // ./tests/ui/new_lint.stdout - /// if_chain! { - /// if let ExprKind::If(ref cond, ref then, None) = item.kind, - /// if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind, - /// if let ExprKind::Path(ref path) = left.kind, - /// if let ExprKind::Lit(ref lit) = right.kind, - /// if let LitKind::Int(42, _) = lit.node, - /// then { - /// // report your lint here - /// } + /// if ExprKind::If(ref cond, ref then, None) = item.kind + /// && let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind + /// && let ExprKind::Path(ref path) = left.kind + /// && let ExprKind::Lit(ref lit) = right.kind + /// && let LitKind::Int(42, _) = lit.node + /// { + /// // report your lint here /// } /// ``` pub LINT_AUTHOR, @@ -91,15 +90,16 @@ macro_rules! field { }; } -fn prelude() { - println!("if_chain! {{"); -} - -fn done() { - println!(" then {{"); - println!(" // report your lint here"); - println!(" }}"); - println!("}}"); +/// Print a condition of a let chain, `chain!(self, "let Some(x) = y")` will print +/// `if let Some(x) = y` on the first call and ` && let Some(x) = y` thereafter +macro_rules! chain { + ($self:ident, $($t:tt)*) => { + if $self.first.take() { + println!("if {}", format_args!($($t)*)); + } else { + println!(" && {}", format_args!($($t)*)); + } + } } impl<'tcx> LateLintPass<'tcx> for Author { @@ -149,9 +149,10 @@ fn check_item(cx: &LateContext<'_>, hir_id: HirId) { fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { if has_attr(cx, hir_id) { - prelude(); f(&PrintVisitor::new(cx)); - done(); + println!("{{"); + println!(" // report your lint here"); + println!("}}"); } } @@ -195,7 +196,9 @@ struct PrintVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, /// Fields are the current index that needs to be appended to pattern /// binding names - ids: std::cell::Cell>, + ids: Cell>, + /// Currently at the first condition in the if chain + first: Cell, } #[allow(clippy::unused_self)] @@ -203,7 +206,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - ids: std::cell::Cell::default(), + ids: Cell::default(), + first: Cell::new(true), } } @@ -226,10 +230,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn option(&self, option: &Binding>, name: &'static str, f: impl Fn(&Binding)) { match option.value { - None => out!("if {option}.is_none();"), + None => chain!(self, "{option}.is_none()"), Some(value) => { let value = &self.bind(name, value); - out!("if let Some({value}) = {option};"); + chain!(self, "let Some({value}) = {option}"); f(value); }, } @@ -237,9 +241,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn slice(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) { if slice.value.is_empty() { - out!("if {slice}.is_empty();"); + chain!(self, "{slice}.is_empty()"); } else { - out!("if {slice}.len() == {};", slice.value.len()); + chain!(self, "{slice}.len() == {}", slice.value.len()); for (i, value) in slice.value.iter().enumerate() { let name = format!("{slice}[{i}]"); f(&Binding { name, value }); @@ -254,23 +258,23 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn ident(&self, ident: &Binding) { - out!("if {ident}.as_str() == {:?};", ident.value.as_str()); + chain!(self, "{ident}.as_str() == {:?}", ident.value.as_str()); } fn symbol(&self, symbol: &Binding) { - out!("if {symbol}.as_str() == {:?};", symbol.value.as_str()); + chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); } fn qpath(&self, qpath: &Binding<&QPath<'_>>) { if let QPath::LangItem(lang_item, ..) = *qpath.value { - out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));"); + chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); } else { - out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value)); + chain!(self, "match_qpath({qpath}, &[{}])", path_to_string(qpath.value)); } } fn lit(&self, lit: &Binding<&Lit>) { - let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;"); + let kind = |kind| chain!(self, "let LitKind::{kind} = {lit}.node"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -298,7 +302,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { LitKind::ByteStr(ref vec) => { bind!(self, vec); kind!("ByteStr(ref {vec})"); - out!("if let [{:?}] = **{vec};", vec.value); + chain!(self, "let [{:?}] = **{vec}", vec.value); }, LitKind::Str(s, _) => { bind!(self, s); @@ -311,15 +315,15 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn arm(&self, arm: &Binding<&hir::Arm<'_>>) { self.pat(field!(arm.pat)); match arm.value.guard { - None => out!("if {arm}.guard.is_none();"), + None => chain!(self, "{arm}.guard.is_none()"), Some(hir::Guard::If(expr)) => { bind!(self, expr); - out!("if let Some(Guard::If({expr})) = {arm}.guard;"); + chain!(self, "let Some(Guard::If({expr})) = {arm}.guard"); self.expr(expr); }, Some(hir::Guard::IfLet(let_expr)) => { bind!(self, let_expr); - out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;"); + chain!(self, "let Some(Guard::IfLet({let_expr}) = {arm}.guard"); self.pat(field!(let_expr.pat)); self.expr(field!(let_expr.init)); }, @@ -331,9 +335,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) { bind!(self, condition, body); - out!( - "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \ - = higher::While::hir({expr});" + chain!( + self, + "let Some(higher::While {{ condition: {condition}, body: {body} }}) \ + = higher::While::hir({expr})" ); self.expr(condition); self.expr(body); @@ -347,9 +352,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }) = higher::WhileLet::hir(expr.value) { bind!(self, let_pat, let_expr, if_then); - out!( - "if let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ - = higher::WhileLet::hir({expr});" + chain!( + self, + "let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ + = higher::WhileLet::hir({expr})" ); self.pat(let_pat); self.expr(let_expr); @@ -359,9 +365,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) { bind!(self, pat, arg, body); - out!( - "if let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ - = higher::ForLoop::hir({expr});" + chain!( + self, + "let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ + = higher::ForLoop::hir({expr})" ); self.pat(pat); self.expr(arg); @@ -369,7 +376,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { return; } - let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;"); + let kind = |kind| chain!(self, "let ExprKind::{kind} = {expr}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -383,7 +390,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { // if it's a path if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { bind!(self, qpath); - out!("if let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind;"); + chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); self.qpath(qpath); } self.expr(field!(let_expr.init)); @@ -419,7 +426,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::Binary(op, left, right) => { bind!(self, op, left, right); kind!("Binary({op}, {left}, {right})"); - out!("if BinOpKind::{:?} == {op}.node;", op.value.node); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); self.expr(left); self.expr(right); }, @@ -438,7 +445,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Cast({expr}, {cast_ty})"); if let TyKind::Path(ref qpath) = cast_ty.value.kind { bind!(self, qpath); - out!("if let TyKind::Path(ref {qpath}) = {cast_ty}.kind;"); + chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); self.qpath(qpath); } self.expr(expr); @@ -485,7 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, fn_decl, body_id); kind!("Closure(CaptureBy::{capture_clause:?}, {fn_decl}, {body_id}, _, {movability})"); - out!("if let {ret_ty} = {fn_decl}.output;"); + chain!(self, "let {ret_ty} = {fn_decl}.output"); self.body(body_id); }, ExprKind::Yield(sub, source) => { @@ -509,7 +516,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { ExprKind::AssignOp(op, target, value) => { bind!(self, op, target, value); kind!("AssignOp({op}, {target}, {value})"); - out!("if BinOpKind::{:?} == {op}.node;", op.value.node); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); self.expr(target); self.expr(value); }, @@ -573,10 +580,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Repeat({value}, {length})"); self.expr(value); match length.value { - ArrayLen::Infer(..) => out!("if let ArrayLen::Infer(..) = length;"), + ArrayLen::Infer(..) => chain!(self, "let ArrayLen::Infer(..) = length"), ArrayLen::Body(anon_const) => { bind!(self, anon_const); - out!("if let ArrayLen::Body({anon_const}) = {length};"); + chain!(self, "let ArrayLen::Body({anon_const}) = {length}"); self.body(field!(anon_const.body)); }, } @@ -600,12 +607,12 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { fn body(&self, body_id: &Binding) { let expr = self.cx.tcx.hir().body(body_id.value).value; bind!(self, expr); - out!("let {expr} = &cx.tcx.hir().body({body_id}).value;"); + chain!(self, "{expr} = &cx.tcx.hir().body({body_id}).value"); self.expr(expr); } fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { - let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;"); + let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } @@ -688,7 +695,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) { - let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;"); + let kind = |kind| chain!(self, "let StmtKind::{kind} = {stmt}.kind"); macro_rules! kind { ($($t:tt)*) => (kind(format_args!($($t)*))); } diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 85bcbc7ad236..71f6c9909ddd 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,1566 +1,12 @@ -use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; -use clippy_utils::consts::{constant_simple, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::match_type; -use clippy_utils::{ - def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_any_def_paths, - match_def_path, method_calls, paths, peel_blocks_with_stmt, peel_hir_expr_refs, SpanlessEq, -}; -use if_chain::if_chain; -use rustc_ast as ast; -use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId}; -use rustc_ast::visit::FnKind; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::hir_id::CRATE_HIR_ID; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{ - BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, - TyKind, UnOp, -}; -use rustc_hir_analysis::hir_ty_to_ty; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::nested_filter; -use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; -use rustc_middle::ty::{ - self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty, -}; -use rustc_semver::RustcVersion; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{sym, BytePos, Span}; - -use std::borrow::{Borrow, Cow}; -use std::str; - -#[cfg(feature = "internal")] +pub mod clippy_lints_internal; +pub mod collapsible_calls; +pub mod compiler_lint_functions; +pub mod if_chain_style; +pub mod interning_defined_symbol; +pub mod invalid_paths; +pub mod lint_without_lint_pass; pub mod metadata_collector; - -declare_clippy_lint! { - /// ### What it does - /// Checks for various things we like to keep tidy in clippy. - /// - /// ### Why is this bad? - /// We like to pretend we're an example of tidy code. - /// - /// ### Example - /// Wrong ordering of the util::paths constants. - pub CLIPPY_LINTS_INTERNAL, - internal, - "various things that will negatively affect your clippy experience" -} - -declare_clippy_lint! { - /// ### What it does - /// Ensures every lint is associated to a `LintPass`. - /// - /// ### Why is this bad? - /// The compiler only knows lints via a `LintPass`. Without - /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not - /// know the name of the lint. - /// - /// ### Known problems - /// Only checks for lints associated using the - /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. - /// - /// ### Example - /// ```rust,ignore - /// declare_lint! { pub LINT_1, ... } - /// declare_lint! { pub LINT_2, ... } - /// declare_lint! { pub FORGOTTEN_LINT, ... } - /// // ... - /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); - /// // missing FORGOTTEN_LINT - /// ``` - pub LINT_WITHOUT_LINT_PASS, - internal, - "declaring a lint without associating it in a LintPass" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` - /// variant of the function. - /// - /// ### Why is this bad? - /// The `utils::*` variants also add a link to the Clippy documentation to the - /// warning/error messages. - /// - /// ### Example - /// ```rust,ignore - /// cx.span_lint(LINT_NAME, "message"); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::span_lint(cx, LINT_NAME, "message"); - /// ``` - pub COMPILER_LINT_FUNCTIONS, - internal, - "usage of the lint functions of the compiler instead of the utils::* variant" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for calls to `cx.outer().expn_data()` and suggests to use - /// the `cx.outer_expn_data()` - /// - /// ### Why is this bad? - /// `cx.outer_expn_data()` is faster and more concise. - /// - /// ### Example - /// ```rust,ignore - /// expr.span.ctxt().outer().expn_data() - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// expr.span.ctxt().outer_expn_data() - /// ``` - pub OUTER_EXPN_EXPN_DATA, - internal, - "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" -} - -declare_clippy_lint! { - /// ### What it does - /// Not an actual lint. This lint is only meant for testing our customized internal compiler - /// error message by calling `panic`. - /// - /// ### Why is this bad? - /// ICE in large quantities can damage your teeth - /// - /// ### Example - /// ```rust,ignore - /// 🍦🍦🍦🍦🍦 - /// ``` - pub PRODUCE_ICE, - internal, - "this message should not appear anywhere as we ICE before and don't emit the lint" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for cases of an auto-generated lint without an updated description, - /// i.e. `default lint description`. - /// - /// ### Why is this bad? - /// Indicates that the lint is not finished. - /// - /// ### Example - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } - /// ``` - pub DEFAULT_LINT, - internal, - "found 'default lint description' in a lint declaration" -} - -declare_clippy_lint! { - /// ### What it does - /// Lints `span_lint_and_then` function calls, where the - /// closure argument has only one statement and that statement is a method - /// call to `span_suggestion`, `span_help`, `span_note` (using the same - /// span), `help` or `note`. - /// - /// These usages of `span_lint_and_then` should be replaced with one of the - /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or - /// `span_lint_and_note`. - /// - /// ### Why is this bad? - /// Using the wrapper `span_lint_and_*` functions, is more - /// convenient, readable and less error prone. - /// - /// ### Example - /// ```rust,ignore - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_suggestion( - /// expr.span, - /// help_msg, - /// sugg.to_string(), - /// Applicability::MachineApplicable, - /// ); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_help(expr.span, help_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.help(help_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_note(expr.span, note_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.note(note_msg); - /// }); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// span_lint_and_sugg( - /// cx, - /// TEST_LINT, - /// expr.span, - /// lint_msg, - /// help_msg, - /// sugg.to_string(), - /// Applicability::MachineApplicable, - /// ); - /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); - /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); - /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); - /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); - /// ``` - pub COLLAPSIBLE_SPAN_LINT_CALLS, - internal, - "found collapsible `span_lint_and_then` calls" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. - /// - /// ### Why is this bad? - /// The path for an item is subject to change and is less efficient to look up than a - /// diagnostic item or a `LangItem`. - /// - /// ### Example - /// ```rust,ignore - /// utils::match_type(cx, ty, &paths::VEC) - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) - /// ``` - pub UNNECESSARY_DEF_PATH, - internal, - "using a def path when a diagnostic item or a `LangItem` is available" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks the paths module for invalid paths. - /// - /// ### Why is this bad? - /// It indicates a bug in the code. - /// - /// ### Example - /// None. - pub INVALID_PATHS, - internal, - "invalid path" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for interning symbols that have already been pre-interned and defined as constants. - /// - /// ### Why is this bad? - /// It's faster and easier to use the symbol constant. - /// - /// ### Example - /// ```rust,ignore - /// let _ = sym!(f32); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// let _ = sym::f32; - /// ``` - pub INTERNING_DEFINED_SYMBOL, - internal, - "interning a symbol that is pre-interned and defined as a constant" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for unnecessary conversion from Symbol to a string. - /// - /// ### Why is this bad? - /// It's faster use symbols directly instead of strings. - /// - /// ### Example - /// ```rust,ignore - /// symbol.as_str() == "clippy"; - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// symbol == sym::clippy; - /// ``` - pub UNNECESSARY_SYMBOL_STR, - internal, - "unnecessary conversion between Symbol and string" -} - -declare_clippy_lint! { - /// Finds unidiomatic usage of `if_chain!` - pub IF_CHAIN_STYLE, - internal, - "non-idiomatic `if_chain!` usage" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for invalid `clippy::version` attributes. - /// - /// Valid values are: - /// * "pre 1.29.0" - /// * any valid semantic version - pub INVALID_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found an invalid `clippy::version` attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for declared clippy lints without the `clippy::version` attribute. - /// - pub MISSING_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found clippy lint without `clippy::version` attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. - /// - pub MISSING_MSRV_ATTR_IMPL, - internal, - "checking if all necessary steps were taken when adding a MSRV to a lint" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for cases of an auto-generated deprecated lint without an updated reason, - /// i.e. `"default deprecation note"`. - /// - /// ### Why is this bad? - /// Indicates that the documentation is incomplete. - /// - /// ### Example - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// TODO - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "default deprecation note" - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// This lint has been replaced by `cooler_lint` - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "this lint has been replaced by `cooler_lint`" - /// } - /// ``` - pub DEFAULT_DEPRECATION_REASON, - internal, - "found 'default deprecation note' in a deprecated lint declaration" -} - -declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); - -impl EarlyLintPass for ClippyLintsInternal { - fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { - if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { - if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { - let mut last_name: Option<&str> = None; - for item in items { - let name = item.ident.as_str(); - if let Some(last_name) = last_name { - if *last_name > *name { - span_lint( - cx, - CLIPPY_LINTS_INTERNAL, - item.span, - "this constant should be before the previous constant due to lexical \ - ordering", - ); - } - } - last_name = Some(name); - } - } - } - } - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct LintWithoutLintPass { - declared_lints: FxHashMap, - registered_lints: FxHashSet, -} - -impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); - -impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) - || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) - { - return; - } - - if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { - let is_lint_ref_ty = is_lint_ref_type(cx, ty); - if is_deprecated_lint(cx, ty) || is_lint_ref_ty { - check_invalid_clippy_version_attribute(cx, item); - - let expr = &cx.tcx.hir().body(body_id).value; - let fields; - if is_lint_ref_ty { - if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind - && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind { - fields = struct_fields; - } else { - return; - } - } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { - fields = struct_fields; - } else { - return; - } - - let field = fields - .iter() - .find(|f| f.ident.as_str() == "desc") - .expect("lints must have a description field"); - - if let ExprKind::Lit(Spanned { - node: LitKind::Str(ref sym, _), - .. - }) = field.expr.kind - { - let sym_str = sym.as_str(); - if is_lint_ref_ty { - if sym_str == "default lint description" { - span_lint( - cx, - DEFAULT_LINT, - item.span, - &format!("the lint `{}` has the default lint description", item.ident.name), - ); - } - - self.declared_lints.insert(item.ident.name, item.span); - } else if sym_str == "default deprecation note" { - span_lint( - cx, - DEFAULT_DEPRECATION_REASON, - item.span, - &format!("the lint `{}` has the default deprecation reason", item.ident.name), - ); - } - } - } - } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { - if !matches!( - cx.tcx.item_name(macro_call.def_id).as_str(), - "impl_lint_pass" | "declare_lint_pass" - ) { - return; - } - if let hir::ItemKind::Impl(hir::Impl { - of_trait: None, - items: impl_item_refs, - .. - }) = item.kind - { - let mut collector = LintCollector { - output: &mut self.registered_lints, - cx, - }; - let body_id = cx.tcx.hir().body_owned_by( - cx.tcx.hir().local_def_id( - impl_item_refs - .iter() - .find(|iiref| iiref.ident.as_str() == "get_lints") - .expect("LintPass needs to implement get_lints") - .id - .hir_id(), - ), - ); - collector.visit_expr(cx.tcx.hir().body(body_id).value); - } - } - } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) { - return; - } - - for (lint_name, &lint_span) in &self.declared_lints { - // When using the `declare_tool_lint!` macro, the original `lint_span`'s - // file points to "". - // `compiletest-rs` thinks that's an error in a different file and - // just ignores it. This causes the test in compile-fail/lint_pass - // not able to capture the error. - // Therefore, we need to climb the macro expansion tree and find the - // actual span that invoked `declare_tool_lint!`: - let lint_span = lint_span.ctxt().outer_expn_data().call_site; - - if !self.registered_lints.contains(lint_name) { - span_lint( - cx, - LINT_WITHOUT_LINT_PASS, - lint_span, - &format!("the lint `{lint_name}` is not added to any `LintPass`"), - ); - } - } - } -} - -fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Rptr( - _, - MutTy { - ty: inner, - mutbl: Mutability::Not, - }, - ) = ty.kind - { - if let TyKind::Path(ref path) = inner.kind { - if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); - } - } - } - - false -} - -fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { - if let Some(value) = extract_clippy_version_value(cx, item) { - // The `sym!` macro doesn't work as it only expects a single token. - // It's better to keep it this way and have a direct `Symbol::intern` call here. - if value == Symbol::intern("pre 1.29.0") { - return; - } - - if RustcVersion::parse(value.as_str()).is_err() { - span_lint_and_help( - cx, - INVALID_CLIPPY_VERSION_ATTRIBUTE, - item.span, - "this item has an invalid `clippy::version` attribute", - None, - "please use a valid semantic version, see `doc/adding_lints.md`", - ); - } - } else { - span_lint_and_help( - cx, - MISSING_CLIPPY_VERSION_ATTRIBUTE, - item.span, - "this lint is missing the `clippy::version` attribute or version value", - None, - "please use a `clippy::version` attribute, see `doc/adding_lints.md`", - ); - } -} - -/// This function extracts the version value of a `clippy::version` attribute if the given value has -/// one -fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - attrs.iter().find_map(|attr| { - if_chain! { - // Identify attribute - if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; - if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; - if tool_name.ident.name == sym::clippy; - if attr_name.ident.name == sym::version; - if let Some(version) = attr.value_str(); - then { - Some(version) - } else { - None - } - } - }) -} - -struct LintCollector<'a, 'tcx> { - output: &'a mut FxHashSet, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { - type NestedFilter = nested_filter::All; - - fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { - if path.segments.len() == 1 { - self.output.insert(path.segments[0].ident.name); - } - } - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } -} - -#[derive(Clone, Default)] -pub struct CompilerLintFunctions { - map: FxHashMap<&'static str, &'static str>, -} - -impl CompilerLintFunctions { - #[must_use] - pub fn new() -> Self { - let mut map = FxHashMap::default(); - map.insert("span_lint", "utils::span_lint"); - map.insert("struct_span_lint", "utils::span_lint"); - map.insert("lint", "utils::span_lint"); - map.insert("span_lint_note", "utils::span_lint_and_note"); - map.insert("span_lint_help", "utils::span_lint_and_help"); - Self { map } - } -} - -impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); - -impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind; - let fn_name = path.ident; - if let Some(sugg) = self.map.get(fn_name.as_str()); - let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, ty, &paths::EARLY_CONTEXT) - || match_type(cx, ty, &paths::LATE_CONTEXT); - then { - span_lint_and_help( - cx, - COMPILER_LINT_FUNCTIONS, - path.ident.span, - "usage of a compiler lint function", - None, - &format!("please use the Clippy variant of this function: `{sugg}`"), - ); - } - } - } -} - -declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); - -impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { - return; - } - - let (method_names, arg_lists, spans) = method_calls(expr, 2); - let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); - if_chain! { - if let ["expn_data", "outer_expn"] = method_names.as_slice(); - let (self_arg, args)= arg_lists[1]; - if args.is_empty(); - let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); - then { - span_lint_and_sugg( - cx, - OUTER_EXPN_EXPN_DATA, - spans[1].with_hi(expr.span.hi()), - "usage of `outer_expn().expn_data()`", - "try", - "outer_expn_data()".to_string(), - Applicability::MachineApplicable, - ); - } - } - } -} - -declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); - -impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { - assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); - } -} - -fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { - match fn_kind { - FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", - FnKind::Closure(..) => false, - } -} - -declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); - -impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(func, and_then_args) = expr.kind; - if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); - if and_then_args.len() == 5; - if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind; - let body = cx.tcx.hir().body(body); - let only_expr = peel_blocks_with_stmt(body.value); - if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; - if let ExprKind::Path(..) = recv.kind; - then { - let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).deny_side_effects(); - match ps.ident.as_str() { - "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); - }, - "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); - }, - "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); - }, - "help" => { - let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); - } - "note" => { - let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); - } - _ => (), - } - } - } - } -} - -struct AndThenSnippets<'a> { - cx: Cow<'a, str>, - lint: Cow<'a, str>, - span: Cow<'a, str>, - msg: Cow<'a, str>, -} - -fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { - let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); - let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); - let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); - let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); - - AndThenSnippets { - cx: cx_snippet, - lint: lint_snippet, - span: span_snippet, - msg: msg_snippet, - } -} - -struct SpanSuggestionSnippets<'a> { - help: Cow<'a, str>, - sugg: Cow<'a, str>, - applicability: Cow<'a, str>, -} - -fn span_suggestion_snippets<'a, 'hir>( - cx: &LateContext<'_>, - span_call_args: &'hir [Expr<'hir>], -) -> SpanSuggestionSnippets<'a> { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - let sugg_snippet = snippet(cx, span_call_args[2].span, ".."); - let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable"); - - SpanSuggestionSnippets { - help: help_snippet, - sugg: sugg_snippet, - applicability: applicability_snippet, - } -} - -fn suggest_suggestion( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - span_suggestion_snippets: &SpanSuggestionSnippets<'_>, -) { - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - span_suggestion_snippets.help, - span_suggestion_snippets.sugg, - span_suggestion_snippets.applicability - ), - Applicability::MachineApplicable, - ); -} - -fn suggest_help( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - help: &str, - with_span: bool, -) { - let option_span = if with_span { - format!("Some({})", and_then_snippets.span) - } else { - "None".to_string() - }; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_help({}, {}, {}, {}, {}, {help})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, - ), - Applicability::MachineApplicable, - ); -} - -fn suggest_note( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - note: &str, - with_span: bool, -) { - let note_span = if with_span { - format!("Some({})", and_then_snippets.span) - } else { - "None".to_string() - }; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, - ), - Applicability::MachineApplicable, - ); -} - -declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); - -#[allow(clippy::too_many_lines)] -impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - enum Item { - LangItem(Symbol), - DiagnosticItem(Symbol), - } - static PATHS: &[&[&str]] = &[ - &["clippy_utils", "match_def_path"], - &["clippy_utils", "match_trait_method"], - &["clippy_utils", "ty", "match_type"], - &["clippy_utils", "is_expr_path_def_path"], - ]; - - if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind; - if let ExprKind::Path(path) = &func.kind; - if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if let Some(which_path) = match_any_def_paths(cx, id, PATHS); - let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; - // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, item_arg); - let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id(); - then { - // def_path_res will match field names before anything else, but for this we want to match - // inherent functions first. - let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field { - let method_name = *segments.last().unwrap(); - cx.tcx.def_key(def_id).parent - .and_then(|parent_idx| - cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter() - .find_map(|impl_id| cx.tcx.associated_items(*impl_id) - .find_by_name_and_kind( - cx.tcx, - Ident::from_str(method_name), - AssocKind::Fn, - *impl_id, - ) - ) - ) - .map_or(def_id, |item| item.def_id) - } else { - def_id - }; - - // Check if the target item is a diagnostic item or LangItem. - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { - let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); - let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name; - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; - }; - - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } - _ => false, - }; - - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => - (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), - has_ctor - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false), - // match_type - (2, Item::DiagnosticItem(item)) => - (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (2, Item::LangItem(item)) => - (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!( - "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})", - ), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!( - "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})", - ), - false, - ), - (3, Item::DiagnosticItem(item)) => - (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", - ), - false, - ), - _ => return, - }; - - span_lint_and_then( - cx, - UNNECESSARY_DEF_PATH, - expr.span, - msg, - |diag| { - diag.span_suggestion(expr.span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead" - ); - } - }, - ); - } - } - } -} - -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { - match peel_hir_expr_refs(expr).0.kind { - ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => { - let parent_id = cx.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { - path_to_matched_type(cx, init) - } else { - None - } - }, - Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( - cx, - cx.tcx.eval_static_initializer(def_id).ok()?.inner(), - cx.tcx.type_of(def_id), - ), - Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { - read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) - }, - _ => None, - }, - _ => None, - }, - ExprKind::Array(exprs) => exprs - .iter() - .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some((*sym.as_str()).to_owned()); - } - } - - None - }) - .collect(), - _ => None, - } -} - -fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { - let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().values().next()?; - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { - (alloc.inner(), ty) - } else { - return None; - } - } else { - (alloc, ty) - }; - - if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() - && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() - && ty.is_str() - { - alloc - .provenance() - .values() - .map(|&alloc| { - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { - let alloc = alloc.inner(); - str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) - .ok().map(ToOwned::to_owned) - } else { - None - } - }) - .collect() - } else { - None - } -} - -// This is not a complete resolver for paths. It works on all the paths currently used in the paths -// module. That's all it does and all it needs to do. -pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if def_path_res(cx, path, None) != Res::Err { - return true; - } - - // Some implementations can't be found by `path_to_res`, particularly inherent - // implementations of native types. Check lang items. - let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); - let lang_items = cx.tcx.lang_items(); - // This list isn't complete, but good enough for our current list of paths. - let incoherent_impls = [ - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), - SimplifiedTypeGen::SliceSimplifiedType, - SimplifiedTypeGen::StrSimplifiedType, - ] - .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); - for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(*item_def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - if matches!( - cx.tcx.def_kind(*item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(*item_def_id) { - if child.ident.name == *item { - return true; - } - } - } else { - for child in cx.tcx.associated_item_def_ids(*item_def_id) { - if cx.tcx.item_name(*child) == *item { - return true; - } - } - } - } - } - } - - false -} - -declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidPaths { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let local_def_id = &cx.tcx.parent_module(item.hir_id()); - let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if_chain! { - if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(ty, body_id) = item.kind; - let ty = hir_ty_to_ty(cx.tcx, ty); - if let ty::Array(el_ty, _) = &ty.kind(); - if let ty::Ref(_, el_ty, _) = &el_ty.kind(); - if el_ty.is_str(); - let body = cx.tcx.hir().body(body_id); - let typeck_results = cx.tcx.typeck_body(body_id); - if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - let path: Vec<&str> = path.iter().map(|x| { - if let Constant::Str(s) = x { - s.as_str() - } else { - // We checked the type of the constant above - unreachable!() - } - }).collect(); - if !check_path(cx, &path[..]); - then { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } - } - } -} - -#[derive(Default)] -pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant DefId. - symbol_map: FxHashMap, -} - -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); - -impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { - fn check_crate(&mut self, cx: &LateContext<'_>) { - if !self.symbol_map.is_empty() { - return; - } - - for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { - for item in cx.tcx.module_children(def_id).iter() { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item_def_id); - } - } - } - } - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, [arg]) = &expr.kind; - if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); - if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); - if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); - let value = Symbol::intern(&arg).as_u32(); - if let Some(&def_id) = self.symbol_map.get(&value); - then { - span_lint_and_sugg( - cx, - INTERNING_DEFINED_SYMBOL, - is_expn_of(expr.span, "sym").unwrap_or(expr.span), - "interning a defined symbol", - "try", - cx.tcx.def_path_str(def_id), - Applicability::MachineApplicable, - ); - } - } - if let ExprKind::Binary(op, left, right) = expr.kind { - if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { - let data = [ - (left, self.symbol_str_expr(left, cx)), - (right, self.symbol_str_expr(right, cx)), - ]; - match data { - // both operands are a symbol string - [(_, Some(left)), (_, Some(right))] => { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary `Symbol` to string conversion", - "try", - format!( - "{} {} {}", - left.as_symbol_snippet(cx), - op.node.as_str(), - right.as_symbol_snippet(cx), - ), - Applicability::MachineApplicable, - ); - }, - // one of the operands is a symbol string - [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { - // creating an owned string for comparison - if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary string allocation", - "try", - format!("{}.as_str()", symbol.as_symbol_snippet(cx)), - Applicability::MachineApplicable, - ); - } - }, - // nothing found - [(_, None), (_, None)] => {}, - } - } - } - } -} - -impl InterningDefinedSymbol { - fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { - static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; - static SYMBOL_STR_PATHS: &[&[&str]] = &[ - &paths::SYMBOL_AS_STR, - &paths::SYMBOL_TO_IDENT_STRING, - &paths::TO_STRING_METHOD, - ]; - let call = if_chain! { - if let ExprKind::AddrOf(_, _, e) = expr.kind; - if let ExprKind::Unary(UnOp::Deref, e) = e.kind; - then { e } else { expr } - }; - if_chain! { - // is a method call - if let ExprKind::MethodCall(_, item, [], _) = call.kind; - if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); - let ty = cx.typeck_results().expr_ty(item); - // ...on either an Ident or a Symbol - if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { - Some(false) - } else if match_type(cx, ty, &paths::IDENT) { - Some(true) - } else { - None - }; - // ...which converts it to a string - let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; - if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); - then { - let is_to_owned = path.last().unwrap().ends_with("string"); - return Some(SymbolStrExpr::Expr { - item, - is_ident, - is_to_owned, - }); - } - } - // is a string constant - if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { - let value = Symbol::intern(&s).as_u32(); - // ...which matches a symbol constant - if let Some(&def_id) = self.symbol_map.get(&value) { - return Some(SymbolStrExpr::Const(def_id)); - } - } - None - } -} - -enum SymbolStrExpr<'tcx> { - /// a string constant with a corresponding symbol constant - Const(DefId), - /// a "symbol to string" expression like `symbol.as_str()` - Expr { - /// part that evaluates to `Symbol` or `Ident` - item: &'tcx Expr<'tcx>, - is_ident: bool, - /// whether an owned `String` is created like `to_ident_string()` - is_to_owned: bool, - }, -} - -impl<'tcx> SymbolStrExpr<'tcx> { - /// Returns a snippet that evaluates to a `Symbol` and is const if possible - fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { - match *self { - Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), - Self::Expr { item, is_ident, .. } => { - let mut snip = snippet(cx, item.span.source_callsite(), ".."); - if is_ident { - // get `Ident.name` - snip.to_mut().push_str(".name"); - } - snip - }, - } - } -} - -declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); - -impl<'tcx> LateLintPass<'tcx> for IfChainStyle { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - let (local, after, if_chain_span) = if_chain! { - if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; - if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); - then { (local, after, if_chain_span) } else { return } - }; - if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be above the `if_chain!`", - ); - } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be inside `then { .. }`", - ); - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { - (cond, then, r#else.is_some()) - } else { - return; - }; - let then_block = match then.kind { - ExprKind::Block(block, _) => block, - _ => return, - }; - let if_chain_span = is_expn_of(expr.span, "if_chain"); - if !els { - check_nested_if_chains(cx, expr, then_block, if_chain_span); - } - let if_chain_span = match if_chain_span { - None => return, - Some(span) => span, - }; - // check for `if a && b;` - if_chain! { - if let ExprKind::Binary(op, _, _) = cond.kind; - if op.node == BinOpKind::And; - if cx.sess().source_map().is_multiline(cond.span); - then { - span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); - } - } - if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) - && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) - { - span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); - } - } -} - -fn check_nested_if_chains( - cx: &LateContext<'_>, - if_expr: &Expr<'_>, - then_block: &Block<'_>, - if_chain_span: Option, -) { - #[rustfmt::skip] - let (head, tail) = match *then_block { - Block { stmts, expr: Some(tail), .. } => (stmts, tail), - Block { - stmts: &[ - ref head @ .., - Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } - ], - .. - } => (head, tail), - _ => return, - }; - if_chain! { - if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); - let sm = cx.sess().source_map(); - if head - .iter() - .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); - if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); - then {} else { return } - } - let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { - (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), - (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), - (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), - _ => return, - }; - span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { - let (span, msg) = match head { - [] => return, - [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), - [a, .., b] => ( - a.span.to(b.span), - "these `let` statements can also be in the `if_chain!`", - ), - }; - diag.span_help(span, msg); - }); -} - -fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { - cx.tcx - .hir() - .parent_iter(hir_id) - .find(|(_, node)| { - #[rustfmt::skip] - !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) - }) - .map_or(false, |(id, _)| { - is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) - }) -} - -/// Checks a trailing slice of statements and expression of a `Block` to see if they are part -/// of the `then {..}` portion of an `if_chain!` -fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { - let span = if let [stmt, ..] = stmts { - stmt.span - } else if let Some(expr) = expr { - expr.span - } else { - // empty `then {}` - return true; - }; - is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) -} - -/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. -fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { - let mut span = local.pat.span; - if let Some(init) = local.init { - span = span.to(init.span); - } - span.adjust(if_chain_span.ctxt().outer_expn()); - let sm = cx.sess().source_map(); - let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span); - let span = sm.span_extend_to_next_char(span, ';', false); - Span::new( - span.lo() - BytePos(3), - span.hi() + BytePos(1), - span.ctxt(), - span.parent(), - ) -} - -declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); - -impl LateLintPass<'_> for MsrvAttrImpl { - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if_chain! { - if let hir::ItemKind::Impl(hir::Impl { - of_trait: Some(lint_pass_trait_ref), - self_ty, - items, - .. - }) = &item.kind; - if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); - let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); - if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); - let self_ty = hir_ty_to_ty(cx.tcx, self_ty); - if let ty::Adt(self_ty_def, _) = self_ty.kind(); - if self_ty_def.is_struct(); - if self_ty_def.all_fields().any(|f| { - cx.tcx - .type_of(f.did) - .walk() - .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) - }); - if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); - then { - let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; - let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; - let span = cx.sess().source_map().span_through_char(item.span, '{'); - span_lint_and_sugg( - cx, - MISSING_MSRV_ATTR_IMPL, - span, - &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), - &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), - format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), - Applicability::MachineApplicable, - ); - } - } - } -} +pub mod msrv_attr_impl; +pub mod outer_expn_data_pass; +pub mod produce_ice; +pub mod unnecessary_def_path; diff --git a/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs b/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs new file mode 100644 index 000000000000..da9514dd15ee --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs @@ -0,0 +1,49 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{Crate, ItemKind, ModKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for various things we like to keep tidy in clippy. + /// + /// ### Why is this bad? + /// We like to pretend we're an example of tidy code. + /// + /// ### Example + /// Wrong ordering of the util::paths constants. + pub CLIPPY_LINTS_INTERNAL, + internal, + "various things that will negatively affect your clippy experience" +} + +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); + +impl EarlyLintPass for ClippyLintsInternal { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { + if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { + if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { + if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { + let mut last_name: Option<&str> = None; + for item in items { + let name = item.ident.as_str(); + if let Some(last_name) = last_name { + if *last_name > *name { + span_lint( + cx, + CLIPPY_LINTS_INTERNAL, + item.span, + "this constant should be before the previous constant due to lexical \ + ordering", + ); + } + } + last_name = Some(name); + } + } + } + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs new file mode 100644 index 000000000000..d7666b77f6e9 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -0,0 +1,245 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::{Closure, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use std::borrow::{Borrow, Cow}; + +declare_clippy_lint! { + /// ### What it does + /// Lints `span_lint_and_then` function calls, where the + /// closure argument has only one statement and that statement is a method + /// call to `span_suggestion`, `span_help`, `span_note` (using the same + /// span), `help` or `note`. + /// + /// These usages of `span_lint_and_then` should be replaced with one of the + /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or + /// `span_lint_and_note`. + /// + /// ### Why is this bad? + /// Using the wrapper `span_lint_and_*` functions, is more + /// convenient, readable and less error prone. + /// + /// ### Example + /// ```rust,ignore + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_suggestion( + /// expr.span, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_help(expr.span, help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.help(help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_note(expr.span, note_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.note(note_msg); + /// }); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// span_lint_and_sugg( + /// cx, + /// TEST_LINT, + /// expr.span, + /// lint_msg, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); + /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); + /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); + /// ``` + pub COLLAPSIBLE_SPAN_LINT_CALLS, + internal, + "found collapsible `span_lint_and_then` calls" +} + +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); + +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::Call(func, and_then_args) = expr.kind; + if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); + if and_then_args.len() == 5; + if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind; + let body = cx.tcx.hir().body(body); + let only_expr = peel_blocks_with_stmt(body.value); + if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; + if let ExprKind::Path(..) = recv.kind; + then { + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); + match ps.ident.as_str() { + "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + suggest_suggestion( + cx, + expr, + &and_then_snippets, + &span_suggestion_snippets(cx, span_call_args), + ); + }, + "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); + }, + "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); + }, + "help" => { + let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); + }, + "note" => { + let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); + }, + _ => (), + } + } + } + } +} + +struct AndThenSnippets<'a> { + cx: Cow<'a, str>, + lint: Cow<'a, str>, + span: Cow<'a, str>, + msg: Cow<'a, str>, +} + +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { + let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); + let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); + let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); + let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); + + AndThenSnippets { + cx: cx_snippet, + lint: lint_snippet, + span: span_snippet, + msg: msg_snippet, + } +} + +struct SpanSuggestionSnippets<'a> { + help: Cow<'a, str>, + sugg: Cow<'a, str>, + applicability: Cow<'a, str>, +} + +fn span_suggestion_snippets<'a, 'hir>( + cx: &LateContext<'_>, + span_call_args: &'hir [Expr<'hir>], +) -> SpanSuggestionSnippets<'a> { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + let sugg_snippet = snippet(cx, span_call_args[2].span, ".."); + let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable"); + + SpanSuggestionSnippets { + help: help_snippet, + sugg: sugg_snippet, + applicability: applicability_snippet, + } +} + +fn suggest_suggestion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + span_suggestion_snippets: &SpanSuggestionSnippets<'_>, +) { + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", + and_then_snippets.cx, + and_then_snippets.lint, + and_then_snippets.span, + and_then_snippets.msg, + span_suggestion_snippets.help, + span_suggestion_snippets.sugg, + span_suggestion_snippets.applicability + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_help( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + help: &str, + with_span: bool, +) { + let option_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_help({}, {}, {}, {}, {}, {help})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_note( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + note: &str, + with_span: bool, +) { + let note_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, + ), + Applicability::MachineApplicable, + ); +} diff --git a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs new file mode 100644 index 000000000000..cacd05262a21 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::match_type; +use clippy_utils::{is_lint_allowed, paths}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` + /// variant of the function. + /// + /// ### Why is this bad? + /// The `utils::*` variants also add a link to the Clippy documentation to the + /// warning/error messages. + /// + /// ### Example + /// ```rust,ignore + /// cx.span_lint(LINT_NAME, "message"); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// utils::span_lint(cx, LINT_NAME, "message"); + /// ``` + pub COMPILER_LINT_FUNCTIONS, + internal, + "usage of the lint functions of the compiler instead of the utils::* variant" +} + +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); + +#[derive(Clone, Default)] +pub struct CompilerLintFunctions { + map: FxHashMap<&'static str, &'static str>, +} + +impl CompilerLintFunctions { + #[must_use] + pub fn new() -> Self { + let mut map = FxHashMap::default(); + map.insert("span_lint", "utils::span_lint"); + map.insert("struct_span_lint", "utils::span_lint"); + map.insert("lint", "utils::span_lint"); + map.insert("span_lint_note", "utils::span_lint_and_note"); + map.insert("span_lint_help", "utils::span_lint_and_help"); + Self { map } + } +} + +impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind; + let fn_name = path.ident; + if let Some(sugg) = self.map.get(fn_name.as_str()); + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); + then { + span_lint_and_help( + cx, + COMPILER_LINT_FUNCTIONS, + path.ident.span, + "usage of a compiler lint function", + None, + &format!("please use the Clippy variant of this function: `{sugg}`"), + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/clippy_lints/src/utils/internal_lints/if_chain_style.rs new file mode 100644 index 000000000000..883a5c08e5c1 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/if_chain_style.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::{higher, is_else_clause, is_expn_of}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Span}; + +declare_clippy_lint! { + /// Finds unidiomatic usage of `if_chain!` + pub IF_CHAIN_STYLE, + internal, + "non-idiomatic `if_chain!` usage" +} + +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); + +impl<'tcx> LateLintPass<'tcx> for IfChainStyle { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + let (local, after, if_chain_span) = if_chain! { + if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; + if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); + then { (local, after, if_chain_span) } else { return } + }; + if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be above the `if_chain!`", + ); + } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be inside `then { .. }`", + ); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { + (cond, then, r#else.is_some()) + } else { + return; + }; + let ExprKind::Block(then_block, _) = then.kind else { return }; + let if_chain_span = is_expn_of(expr.span, "if_chain"); + if !els { + check_nested_if_chains(cx, expr, then_block, if_chain_span); + } + let Some(if_chain_span) = if_chain_span else { return }; + // check for `if a && b;` + if_chain! { + if let ExprKind::Binary(op, _, _) = cond.kind; + if op.node == BinOpKind::And; + if cx.sess().source_map().is_multiline(cond.span); + then { + span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); + } + } + if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) + && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) + { + span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); + } + } +} + +fn check_nested_if_chains( + cx: &LateContext<'_>, + if_expr: &Expr<'_>, + then_block: &Block<'_>, + if_chain_span: Option, +) { + #[rustfmt::skip] + let (head, tail) = match *then_block { + Block { stmts, expr: Some(tail), .. } => (stmts, tail), + Block { + stmts: &[ + ref head @ .., + Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } + ], + .. + } => (head, tail), + _ => return, + }; + if_chain! { + if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); + let sm = cx.sess().source_map(); + if head + .iter() + .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); + if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); + then { + } else { + return; + } + } + let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { + (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), + (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), + (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), + _ => return, + }; + span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { + let (span, msg) = match head { + [] => return, + [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), + [a, .., b] => ( + a.span.to(b.span), + "these `let` statements can also be in the `if_chain!`", + ), + }; + diag.span_help(span, msg); + }); +} + +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { + cx.tcx + .hir() + .parent_iter(hir_id) + .find(|(_, node)| { + #[rustfmt::skip] + !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) + }) + .map_or(false, |(id, _)| { + is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) + }) +} + +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part +/// of the `then {..}` portion of an `if_chain!` +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { + let span = if let [stmt, ..] = stmts { + stmt.span + } else if let Some(expr) = expr { + expr.span + } else { + // empty `then {}` + return true; + }; + is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) +} + +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { + let mut span = local.pat.span; + if let Some(init) = local.init { + span = span.to(init.span); + } + span.adjust(if_chain_span.ctxt().outer_expn()); + let sm = cx.sess().source_map(); + let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span); + let span = sm.span_extend_to_next_char(span, ';', false); + Span::new( + span.lo() - BytePos(3), + span.hi() + BytePos(1), + span.ctxt(), + span.parent(), + ) +} diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs new file mode 100644 index 000000000000..096b601572b4 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -0,0 +1,239 @@ +use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::match_type; +use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::{self}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::Symbol; + +use std::borrow::Cow; + +declare_clippy_lint! { + /// ### What it does + /// Checks for interning symbols that have already been pre-interned and defined as constants. + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. + /// + /// ### Example + /// ```rust,ignore + /// let _ = sym!(f32); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub INTERNING_DEFINED_SYMBOL, + internal, + "interning a symbol that is pre-interned and defined as a constant" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for unnecessary conversion from Symbol to a string. + /// + /// ### Why is this bad? + /// It's faster use symbols directly instead of strings. + /// + /// ### Example + /// ```rust,ignore + /// symbol.as_str() == "clippy"; + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// symbol == sym::clippy; + /// ``` + pub UNNECESSARY_SYMBOL_STR, + internal, + "unnecessary conversion between Symbol and string" +} + +#[derive(Default)] +pub struct InterningDefinedSymbol { + // Maps the symbol value to the constant DefId. + symbol_map: FxHashMap, +} + +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); + +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { + fn check_crate(&mut self, cx: &LateContext<'_>) { + if !self.symbol_map.is_empty() { + return; + } + + for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { + if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { + for item in cx.tcx.module_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + self.symbol_map.insert(value, item_def_id); + } + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(func, [arg]) = &expr.kind; + if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); + if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); + if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); + let value = Symbol::intern(&arg).as_u32(); + if let Some(&def_id) = self.symbol_map.get(&value); + then { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + cx.tcx.def_path_str(def_id), + Applicability::MachineApplicable, + ); + } + } + if let ExprKind::Binary(op, left, right) = expr.kind { + if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { + let data = [ + (left, self.symbol_str_expr(left, cx)), + (right, self.symbol_str_expr(right, cx)), + ]; + match data { + // both operands are a symbol string + [(_, Some(left)), (_, Some(right))] => { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary `Symbol` to string conversion", + "try", + format!( + "{} {} {}", + left.as_symbol_snippet(cx), + op.node.as_str(), + right.as_symbol_snippet(cx), + ), + Applicability::MachineApplicable, + ); + }, + // one of the operands is a symbol string + [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { + // creating an owned string for comparison + if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary string allocation", + "try", + format!("{}.as_str()", symbol.as_symbol_snippet(cx)), + Applicability::MachineApplicable, + ); + } + }, + // nothing found + [(_, None), (_, None)] => {}, + } + } + } + } +} + +impl InterningDefinedSymbol { + fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { + static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; + static SYMBOL_STR_PATHS: &[&[&str]] = &[ + &paths::SYMBOL_AS_STR, + &paths::SYMBOL_TO_IDENT_STRING, + &paths::TO_STRING_METHOD, + ]; + let call = if_chain! { + if let ExprKind::AddrOf(_, _, e) = expr.kind; + if let ExprKind::Unary(UnOp::Deref, e) = e.kind; + then { e } else { expr } + }; + if_chain! { + // is a method call + if let ExprKind::MethodCall(_, item, [], _) = call.kind; + if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); + let ty = cx.typeck_results().expr_ty(item); + // ...on either an Ident or a Symbol + if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + Some(false) + } else if match_type(cx, ty, &paths::IDENT) { + Some(true) + } else { + None + }; + // ...which converts it to a string + let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; + if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); + then { + let is_to_owned = path.last().unwrap().ends_with("string"); + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); + } + } + // is a string constant + if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + let value = Symbol::intern(&s).as_u32(); + // ...which matches a symbol constant + if let Some(&def_id) = self.symbol_map.get(&value) { + return Some(SymbolStrExpr::Const(def_id)); + } + } + None + } +} + +enum SymbolStrExpr<'tcx> { + /// a string constant with a corresponding symbol constant + Const(DefId), + /// a "symbol to string" expression like `symbol.as_str()` + Expr { + /// part that evaluates to `Symbol` or `Ident` + item: &'tcx Expr<'tcx>, + is_ident: bool, + /// whether an owned `String` is created like `to_ident_string()` + is_to_owned: bool, + }, +} + +impl<'tcx> SymbolStrExpr<'tcx> { + /// Returns a snippet that evaluates to a `Symbol` and is const if possible + fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { + match *self { + Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), + Self::Expr { item, is_ident, .. } => { + let mut snip = snippet(cx, item.span.source_callsite(), ".."); + if is_ident { + // get `Ident.name` + snip.to_mut().push_str(".name"); + } + snip + }, + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs new file mode 100644 index 000000000000..25532dd4e268 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -0,0 +1,108 @@ +use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::def_path_res; +use clippy_utils::diagnostics::span_lint; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::Item; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, FloatTy}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks the paths module for invalid paths. + /// + /// ### Why is this bad? + /// It indicates a bug in the code. + /// + /// ### Example + /// None. + pub INVALID_PATHS, + internal, + "invalid path" +} + +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidPaths { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let local_def_id = &cx.tcx.parent_module(item.hir_id()); + let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); + if_chain! { + if mod_name.as_str() == "paths"; + if let hir::ItemKind::Const(ty, body_id) = item.kind; + let ty = hir_ty_to_ty(cx.tcx, ty); + if let ty::Array(el_ty, _) = &ty.kind(); + if let ty::Ref(_, el_ty, _) = &el_ty.kind(); + if el_ty.is_str(); + let body = cx.tcx.hir().body(body_id); + let typeck_results = cx.tcx.typeck_body(body_id); + if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); + let path: Vec<&str> = path + .iter() + .map(|x| { + if let Constant::Str(s) = x { + s.as_str() + } else { + // We checked the type of the constant above + unreachable!() + } + }) + .collect(); + if !check_path(cx, &path[..]); + then { + span_lint(cx, INVALID_PATHS, item.span, "invalid path"); + } + } + } +} + +// This is not a complete resolver for paths. It works on all the paths currently used in the paths +// module. That's all it does and all it needs to do. +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { + if def_path_res(cx, path, None) != Res::Err { + return true; + } + + // Some implementations can't be found by `path_to_res`, particularly inherent + // implementations of native types. Check lang items. + let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); + let lang_items = cx.tcx.lang_items(); + // This list isn't complete, but good enough for our current list of paths. + let incoherent_impls = [ + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), + SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), + SimplifiedTypeGen::SliceSimplifiedType, + SimplifiedTypeGen::StrSimplifiedType, + ] + .iter() + .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); + for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { + let lang_item_path = cx.get_def_path(*item_def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + if matches!( + cx.tcx.def_kind(*item_def_id), + DefKind::Mod | DefKind::Enum | DefKind::Trait + ) { + for child in cx.tcx.module_children(*item_def_id) { + if child.ident.name == *item { + return true; + } + } + } else { + for child in cx.tcx.associated_item_def_ids(*item_def_id) { + if cx.tcx.item_name(*child) == *item { + return true; + } + } + } + } + } + } + + false +} diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs new file mode 100644 index 000000000000..0dac64376b06 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -0,0 +1,342 @@ +use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::{is_lint_allowed, match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast as ast; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::hir_id::CRATE_HIR_ID; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Ensures every lint is associated to a `LintPass`. + /// + /// ### Why is this bad? + /// The compiler only knows lints via a `LintPass`. Without + /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not + /// know the name of the lint. + /// + /// ### Known problems + /// Only checks for lints associated using the + /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. + /// + /// ### Example + /// ```rust,ignore + /// declare_lint! { pub LINT_1, ... } + /// declare_lint! { pub LINT_2, ... } + /// declare_lint! { pub FORGOTTEN_LINT, ... } + /// // ... + /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); + /// // missing FORGOTTEN_LINT + /// ``` + pub LINT_WITHOUT_LINT_PASS, + internal, + "declaring a lint without associating it in a LintPass" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases of an auto-generated lint without an updated description, + /// i.e. `default lint description`. + /// + /// ### Why is this bad? + /// Indicates that the lint is not finished. + /// + /// ### Example + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } + /// ``` + pub DEFAULT_LINT, + internal, + "found 'default lint description' in a lint declaration" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for invalid `clippy::version` attributes. + /// + /// Valid values are: + /// * "pre 1.29.0" + /// * any valid semantic version + pub INVALID_CLIPPY_VERSION_ATTRIBUTE, + internal, + "found an invalid `clippy::version` attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for declared clippy lints without the `clippy::version` attribute. + /// + pub MISSING_CLIPPY_VERSION_ATTRIBUTE, + internal, + "found clippy lint without `clippy::version` attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases of an auto-generated deprecated lint without an updated reason, + /// i.e. `"default deprecation note"`. + /// + /// ### Why is this bad? + /// Indicates that the documentation is incomplete. + /// + /// ### Example + /// ```rust,ignore + /// declare_deprecated_lint! { + /// /// ### What it does + /// /// Nothing. This lint has been deprecated. + /// /// + /// /// ### Deprecation reason + /// /// TODO + /// #[clippy::version = "1.63.0"] + /// pub COOL_LINT, + /// "default deprecation note" + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// declare_deprecated_lint! { + /// /// ### What it does + /// /// Nothing. This lint has been deprecated. + /// /// + /// /// ### Deprecation reason + /// /// This lint has been replaced by `cooler_lint` + /// #[clippy::version = "1.63.0"] + /// pub COOL_LINT, + /// "this lint has been replaced by `cooler_lint`" + /// } + /// ``` + pub DEFAULT_DEPRECATION_REASON, + internal, + "found 'default deprecation note' in a deprecated lint declaration" +} + +#[derive(Clone, Debug, Default)] +pub struct LintWithoutLintPass { + declared_lints: FxHashMap, + registered_lints: FxHashSet, +} + +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); + +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) + || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) + { + return; + } + + if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { + let is_lint_ref_ty = is_lint_ref_type(cx, ty); + if is_deprecated_lint(cx, ty) || is_lint_ref_ty { + check_invalid_clippy_version_attribute(cx, item); + + let expr = &cx.tcx.hir().body(body_id).value; + let fields; + if is_lint_ref_ty { + if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind + && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind { + fields = struct_fields; + } else { + return; + } + } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { + fields = struct_fields; + } else { + return; + } + + let field = fields + .iter() + .find(|f| f.ident.as_str() == "desc") + .expect("lints must have a description field"); + + if let ExprKind::Lit(Spanned { + node: LitKind::Str(ref sym, _), + .. + }) = field.expr.kind + { + let sym_str = sym.as_str(); + if is_lint_ref_ty { + if sym_str == "default lint description" { + span_lint( + cx, + DEFAULT_LINT, + item.span, + &format!("the lint `{}` has the default lint description", item.ident.name), + ); + } + + self.declared_lints.insert(item.ident.name, item.span); + } else if sym_str == "default deprecation note" { + span_lint( + cx, + DEFAULT_DEPRECATION_REASON, + item.span, + &format!("the lint `{}` has the default deprecation reason", item.ident.name), + ); + } + } + } + } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { + if !matches!( + cx.tcx.item_name(macro_call.def_id).as_str(), + "impl_lint_pass" | "declare_lint_pass" + ) { + return; + } + if let hir::ItemKind::Impl(hir::Impl { + of_trait: None, + items: impl_item_refs, + .. + }) = item.kind + { + let mut collector = LintCollector { + output: &mut self.registered_lints, + cx, + }; + let body_id = cx.tcx.hir().body_owned_by( + cx.tcx.hir().local_def_id( + impl_item_refs + .iter() + .find(|iiref| iiref.ident.as_str() == "get_lints") + .expect("LintPass needs to implement get_lints") + .id + .hir_id(), + ), + ); + collector.visit_expr(cx.tcx.hir().body(body_id).value); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) { + return; + } + + for (lint_name, &lint_span) in &self.declared_lints { + // When using the `declare_tool_lint!` macro, the original `lint_span`'s + // file points to "". + // `compiletest-rs` thinks that's an error in a different file and + // just ignores it. This causes the test in compile-fail/lint_pass + // not able to capture the error. + // Therefore, we need to climb the macro expansion tree and find the + // actual span that invoked `declare_tool_lint!`: + let lint_span = lint_span.ctxt().outer_expn_data().call_site; + + if !self.registered_lints.contains(lint_name) { + span_lint( + cx, + LINT_WITHOUT_LINT_PASS, + lint_span, + &format!("the lint `{lint_name}` is not added to any `LintPass`"), + ); + } + } + } +} + +pub(super) fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { + if let TyKind::Rptr( + _, + MutTy { + ty: inner, + mutbl: Mutability::Not, + }, + ) = ty.kind + { + if let TyKind::Path(ref path) = inner.kind { + if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { + return match_def_path(cx, def_id, &paths::LINT); + } + } + } + + false +} + +fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { + if let Some(value) = extract_clippy_version_value(cx, item) { + // The `sym!` macro doesn't work as it only expects a single token. + // It's better to keep it this way and have a direct `Symbol::intern` call here. + if value == Symbol::intern("pre 1.29.0") { + return; + } + + if RustcVersion::parse(value.as_str()).is_err() { + span_lint_and_help( + cx, + INVALID_CLIPPY_VERSION_ATTRIBUTE, + item.span, + "this item has an invalid `clippy::version` attribute", + None, + "please use a valid semantic version, see `doc/adding_lints.md`", + ); + } + } else { + span_lint_and_help( + cx, + MISSING_CLIPPY_VERSION_ATTRIBUTE, + item.span, + "this lint is missing the `clippy::version` attribute or version value", + None, + "please use a `clippy::version` attribute, see `doc/adding_lints.md`", + ); + } +} + +/// This function extracts the version value of a `clippy::version` attribute if the given value has +/// one +pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + attrs.iter().find_map(|attr| { + if_chain! { + // Identify attribute + if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; + if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; + if tool_name.ident.name == sym::clippy; + if attr_name.ident.name == sym::version; + if let Some(version) = attr.value_str(); + then { Some(version) } else { None } + } + }) +} + +struct LintCollector<'a, 'tcx> { + output: &'a mut FxHashSet, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { + type NestedFilter = nested_filter::All; + + fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { + if path.segments.len() == 1 { + self.output.insert(path.segments[0].ident.name); + } + } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } +} diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c84191bb0103..d06a616e4b30 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,7 +8,7 @@ //! a simple mistake) use crate::renamed_lints::RENAMED_LINTS; -use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type}; +use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; @@ -532,7 +532,11 @@ fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec, String)> { // Extract lints doc_comment.make_ascii_lowercase(); - let lints: Vec = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect(); + let lints: Vec = doc_comment + .split_off(DOC_START.len()) + .split(", ") + .map(str::to_string) + .collect(); // Format documentation correctly // split off leading `.` from lint name list and indent for correct formatting diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs new file mode 100644 index 000000000000..1e994e3f2b17 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::match_type; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::{self, subst::GenericArgKind}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. + /// + pub MISSING_MSRV_ATTR_IMPL, + internal, + "checking if all necessary steps were taken when adding a MSRV to a lint" +} + +declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); + +impl LateLintPass<'_> for MsrvAttrImpl { + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if_chain! { + if let hir::ItemKind::Impl(hir::Impl { + of_trait: Some(lint_pass_trait_ref), + self_ty, + items, + .. + }) = &item.kind; + if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); + let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); + if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); + let self_ty = hir_ty_to_ty(cx.tcx, self_ty); + if let ty::Adt(self_ty_def, _) = self_ty.kind(); + if self_ty_def.is_struct(); + if self_ty_def.all_fields().any(|f| { + cx.tcx + .type_of(f.did) + .walk() + .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) + .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) + }); + if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); + then { + let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; + let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; + let span = cx.sess().source_map().span_through_char(item.span, '{'); + span_lint_and_sugg( + cx, + MISSING_MSRV_ATTR_IMPL, + span, + &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), + &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), + format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs new file mode 100644 index 000000000000..2b13fad80665 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs @@ -0,0 +1,62 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::match_type; +use clippy_utils::{is_lint_allowed, method_calls, paths}; +use if_chain::if_chain; +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_span::symbol::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `cx.outer().expn_data()` and suggests to use + /// the `cx.outer_expn_data()` + /// + /// ### Why is this bad? + /// `cx.outer_expn_data()` is faster and more concise. + /// + /// ### Example + /// ```rust,ignore + /// expr.span.ctxt().outer().expn_data() + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// expr.span.ctxt().outer_expn_data() + /// ``` + pub OUTER_EXPN_EXPN_DATA, + internal, + "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" +} + +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); + +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { + return; + } + + let (method_names, arg_lists, spans) = method_calls(expr, 2); + let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); + if_chain! { + if let ["expn_data", "outer_expn"] = method_names.as_slice(); + let (self_arg, args) = arg_lists[1]; + if args.is_empty(); + let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); + then { + span_lint_and_sugg( + cx, + OUTER_EXPN_EXPN_DATA, + spans[1].with_hi(expr.span.hi()), + "usage of `outer_expn().expn_data()`", + "try", + "outer_expn_data()".to_string(), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/produce_ice.rs b/clippy_lints/src/utils/internal_lints/produce_ice.rs new file mode 100644 index 000000000000..5899b94e16ba --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/produce_ice.rs @@ -0,0 +1,37 @@ +use rustc_ast::ast::NodeId; +use rustc_ast::visit::FnKind; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Not an actual lint. This lint is only meant for testing our customized internal compiler + /// error message by calling `panic`. + /// + /// ### Why is this bad? + /// ICE in large quantities can damage your teeth + /// + /// ### Example + /// ```rust,ignore + /// 🍦🍦🍦🍦🍦 + /// ``` + pub PRODUCE_ICE, + internal, + "this message should not appear anywhere as we ICE before and don't emit the lint" +} + +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); + +impl EarlyLintPass for ProduceIce { + fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { + assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); + } +} + +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { + match fn_kind { + FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", + FnKind::Closure(..) => false, + } +} diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs new file mode 100644 index 000000000000..4cf76f536255 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -0,0 +1,343 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; +use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +use std::str; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. + /// + /// ### Why is this bad? + /// The path for an item is subject to change and is less efficient to look up than a + /// diagnostic item or a `LangItem`. + /// + /// ### Example + /// ```rust,ignore + /// utils::match_type(cx, ty, &paths::VEC) + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) + /// ``` + pub UNNECESSARY_DEF_PATH, + internal, + "using a def path when a diagnostic item or a `LangItem` is available" +} + +impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); + +#[derive(Default)] +pub struct UnnecessaryDefPath { + array_def_ids: FxHashSet<(DefId, Span)>, + linted_def_ids: FxHashSet, +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { + return; + } + + match expr.kind { + ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span), + ExprKind::Array(elements) => self.check_array(cx, elements, expr.span), + _ => {}, + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for &(def_id, span) in &self.array_def_ids { + if self.linted_def_ids.contains(&def_id) { + continue; + } + + let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) { + ("diagnostic item", format!("sym::{sym}")) + } else if let Some(sym) = get_lang_item_name(cx, def_id) { + ("language item", format!("LangItem::{sym}")) + } else { + continue; + }; + + span_lint_and_help( + cx, + UNNECESSARY_DEF_PATH, + span, + &format!("hardcoded path to a {msg}"), + None, + &format!("convert all references to use `{sugg}`"), + ); + } + } +} + +impl UnnecessaryDefPath { + #[allow(clippy::too_many_lines)] + fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { + enum Item { + LangItem(Symbol), + DiagnosticItem(Symbol), + } + static PATHS: &[&[&str]] = &[ + &["clippy_utils", "match_def_path"], + &["clippy_utils", "match_trait_method"], + &["clippy_utils", "ty", "match_type"], + &["clippy_utils", "is_expr_path_def_path"], + ]; + + if_chain! { + if let [cx_arg, def_arg, args @ ..] = args; + if let ExprKind::Path(path) = &func.kind; + if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if let Some(which_path) = match_any_def_paths(cx, id, PATHS); + let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; + // Extract the path to the matched type + if let Some(segments) = path_to_matched_type(cx, item_arg); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(def_id) = inherent_def_path_res(cx, &segments[..]); + then { + // Check if the target item is a diagnostic item or LangItem. + #[rustfmt::skip] + let (msg, item) = if let Some(item_name) + = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) + { + ( + "use of a def path to a diagnostic item", + Item::DiagnosticItem(*item_name), + ) + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { + ( + "use of a def path to a `LangItem`", + Item::LangItem(item_name), + ) + } else { + return; + }; + + let has_ctor = match cx.tcx.def_kind(def_id) { + DefKind::Struct => { + let variant = cx.tcx.adt_def(def_id).non_enum_variant(); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + DefKind::Variant => { + let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); + variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + _ => false, + }; + + let mut app = Applicability::MachineApplicable; + let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); + let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); + let (sugg, with_note) = match (which_path, item) { + // match_def_path + (0, Item::DiagnosticItem(item)) => ( + format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), + has_ctor, + ), + (0, Item::LangItem(item)) => ( + format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), + has_ctor, + ), + // match_trait_method + (1, Item::DiagnosticItem(item)) => { + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) + }, + // match_type + (2, Item::DiagnosticItem(item)) => ( + format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (2, Item::LangItem(item)) => ( + format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), + false, + ), + // is_expr_path_def_path + (3, Item::DiagnosticItem(item)) if has_ctor => ( + format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), + false, + ), + (3, Item::LangItem(item)) if has_ctor => ( + format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), + false, + ), + (3, Item::DiagnosticItem(item)) => ( + format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (3, Item::LangItem(item)) => ( + format!( + "path_res({cx_snip}, {def_snip}).opt_def_id()\ + .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", + ), + false, + ), + _ => return, + }; + + span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { + diag.span_suggestion(span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead", + ); + } + }); + + self.linted_def_ids.insert(def_id); + } + } + } + + fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { + let Some(path) = path_from_array(elements) else { return }; + + if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::>()) { + self.array_def_ids.insert((def_id, span)); + } + } +} + +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + match peel_hir_expr_refs(expr).0.kind { + ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { + Res::Local(hir_id) => { + let parent_id = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { + path_to_matched_type(cx, init) + } else { + None + } + }, + Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( + cx, + cx.tcx.eval_static_initializer(def_id).ok()?.inner(), + cx.tcx.type_of(def_id), + ), + Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { + ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { + read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) + }, + _ => None, + }, + _ => None, + }, + ExprKind::Array(exprs) => path_from_array(exprs), + _ => None, + } +} + +fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { + let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { + let &alloc = alloc.provenance().values().next()?; + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + (alloc.inner(), ty) + } else { + return None; + } + } else { + (alloc, ty) + }; + + if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() + && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() + && ty.is_str() + { + alloc + .provenance() + .values() + .map(|&alloc| { + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + let alloc = alloc.inner(); + str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) + .ok().map(ToOwned::to_owned) + } else { + None + } + }) + .collect() + } else { + None + } +} + +fn path_from_array(exprs: &[Expr<'_>]) -> Option> { + exprs + .iter() + .map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some((*sym.as_str()).to_owned()); + } + } + + None + }) + .collect() +} + +// def_path_res will match field names before anything else, but for this we want to match +// inherent functions first. +fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option { + def_path_res(cx, segments, None).opt_def_id().map(|def_id| { + if cx.tcx.def_kind(def_id) == DefKind::Field { + let method_name = *segments.last().unwrap(); + cx.tcx + .def_key(def_id) + .parent + .and_then(|parent_idx| { + cx.tcx + .inherent_impls(DefId { + index: parent_idx, + krate: def_id.krate, + }) + .iter() + .find_map(|impl_id| { + cx.tcx.associated_items(*impl_id).find_by_name_and_kind( + cx.tcx, + Ident::from_str(method_name), + AssocKind::Fn, + *impl_id, + ) + }) + }) + .map_or(def_id, |item| item.def_id) + } else { + def_id + } + }) +} + +fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option { + if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { + let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); + let item_name = cx + .tcx + .adt_def(lang_items) + .variants() + .iter() + .nth(lang_item) + .unwrap() + .name; + Some(item_name) + } else { + None + } +} diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index d9b22664fd25..cd8575c90e86 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -136,7 +136,7 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s .emit(); }, ast::AttrStyle::Outer => { - sess.span_err(attr.span, &format!("`{name}` cannot be an outer attribute")); + sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute")); }, } } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index fa6766f7cfe1..07e4ef6a2fef 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -136,17 +136,49 @@ impl Constant { (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), - (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => iter::zip(l, r) - .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) - .find(|r| r.map_or(true, |o| o != Ordering::Equal)) - .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), + (&Self::Tuple(ref l), &Self::Tuple(ref r)) if l.len() == r.len() => match *cmp_type.kind() { + ty::Tuple(tys) if tys.len() == l.len() => l + .iter() + .zip(r) + .zip(tys) + .map(|((li, ri), cmp_type)| Self::partial_cmp(tcx, cmp_type, li, ri)) + .find(|r| r.map_or(true, |o| o != Ordering::Equal)) + .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), + _ => None, + }, + (&Self::Vec(ref l), &Self::Vec(ref r)) => { + let cmp_type = match *cmp_type.kind() { + ty::Array(ty, _) | ty::Slice(ty) => ty, + _ => return None, + }; + iter::zip(l, r) + .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) + .find(|r| r.map_or(true, |o| o != Ordering::Equal)) + .unwrap_or_else(|| Some(l.len().cmp(&r.len()))) + }, (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { - match Self::partial_cmp(tcx, cmp_type, lv, rv) { + match Self::partial_cmp( + tcx, + match *cmp_type.kind() { + ty::Array(ty, _) => ty, + _ => return None, + }, + lv, + rv, + ) { Some(Equal) => Some(ls.cmp(rs)), x => x, } }, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb), + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp( + tcx, + match *cmp_type.kind() { + ty::Ref(_, ty, _) => ty, + _ => return None, + }, + lb, + rb, + ), // TODO: are there any useful inter-type orderings? _ => None, } diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 8724a4cd651d..95b3e651e2b5 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -120,7 +120,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .expr_ty(e) .has_significant_drop(self.cx.tcx, self.cx.param_env) { - self.eagerness = Lazy; + self.eagerness = ForceNoChange; return; } }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7e42fcc6569b..052db3f3a039 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -25,10 +25,12 @@ extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_typeck; +extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; +extern crate rustc_mir_dataflow; extern crate rustc_parse_format; extern crate rustc_session; extern crate rustc_span; @@ -48,6 +50,7 @@ pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod macros; +pub mod mir; pub mod msrvs; pub mod numeric_literal; pub mod paths; @@ -122,7 +125,7 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt return Some(version); } else if let Some(sess) = sess { if let Some(span) = span { - sess.span_err(span, &format!("`{msrv}` is not a valid Rust version")); + sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); } } None @@ -815,13 +818,37 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { false } }, - ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func), + ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func), + ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg), ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone), ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), _ => false, } } +fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool { + if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind && + seg.ident.name == sym::from + { + match arg.kind { + ExprKind::Lit(hir::Lit { + node: LitKind::Str(ref sym, _), + .. + }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String), + ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), + ExprKind::Repeat(_, ArrayLen::Body(len)) => { + if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind && + let LitKind::Int(v, _) = const_lit.node + { + return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec); + } + } + _ => (), + } + } + false +} + /// Checks if the top level expression can be moved into a closure as is. /// Currently checks for: /// * Break/Continue outside the given loop HIR ids. @@ -1739,6 +1766,7 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool /// ```rust,ignore /// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX); /// ``` +/// This function is deprecated. Use [`match_function_call_with_def_id`]. pub fn match_function_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -1756,6 +1784,22 @@ pub fn match_function_call<'tcx>( None } +pub fn match_function_call_with_def_id<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + fun_def_id: DefId, +) -> Option<&'tcx [Expr<'tcx>]> { + if_chain! { + if let ExprKind::Call(fun, args) = expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id); + then { + return Some(args); + } + }; + None +} + /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if /// any. /// diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 5a63c290a315..9a682fbe604f 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -627,7 +627,7 @@ pub enum Count<'tcx> { /// `FormatParamKind::Numbered`. Param(FormatParam<'tcx>), /// Not specified. - Implied, + Implied(Option), } impl<'tcx> Count<'tcx> { @@ -638,8 +638,10 @@ impl<'tcx> Count<'tcx> { inner: Option, values: &FormatArgsValues<'tcx>, ) -> Option { + let span = inner.map(|inner| span_from_inner(values.format_string_span, inner)); + Some(match count { - rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)), + rpf::Count::CountIs(val) => Self::Is(val, span?), rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new( FormatParamKind::Named(Symbol::intern(name)), usage, @@ -661,12 +663,12 @@ impl<'tcx> Count<'tcx> { inner?, values, )?), - rpf::Count::CountImplied => Self::Implied, + rpf::Count::CountImplied => Self::Implied(span), }) } pub fn is_implied(self) -> bool { - matches!(self, Count::Implied) + matches!(self, Count::Implied(_)) } pub fn param(self) -> Option> { @@ -675,6 +677,14 @@ impl<'tcx> Count<'tcx> { _ => None, } } + + pub fn span(self) -> Option { + match self { + Count::Is(_, span) => Some(span), + Count::Param(param) => Some(param.span), + Count::Implied(span) => span, + } + } } /// Specification for the formatting of an argument in the format string. See @@ -738,8 +748,13 @@ impl<'tcx> FormatSpec<'tcx> { /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`, /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}` pub fn is_default(&self) -> bool { - self.r#trait == sym::Display - && self.width.is_implied() + self.r#trait == sym::Display && self.is_default_for_trait() + } + + /// Has no other formatting specifiers than setting the format trait. returns true for `{}`, + /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}` + pub fn is_default_for_trait(&self) -> bool { + self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.flags == 0 @@ -757,6 +772,22 @@ pub struct FormatArg<'tcx> { pub span: Span, } +impl<'tcx> FormatArg<'tcx> { + /// Span of the `:` and format specifiers + /// + /// ```ignore + /// format!("{:.}"), format!("{foo:.}") + /// ^^ ^^ + /// ``` + pub fn format_span(&self) -> Span { + let base = self.span.data(); + + // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing + // brace `{...|}` + Span::new(self.param.span.hi(), base.hi - BytePos(1), base.ctxt, base.parent) + } +} + /// A parsed `format_args!` expansion. #[derive(Debug)] pub struct FormatArgsExpn<'tcx> { diff --git a/clippy_utils/src/mir/maybe_storage_live.rs b/clippy_utils/src/mir/maybe_storage_live.rs new file mode 100644 index 000000000000..d262b335d99d --- /dev/null +++ b/clippy_utils/src/mir/maybe_storage_live.rs @@ -0,0 +1,52 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir; +use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis}; + +/// Determines liveness of each local purely based on `StorageLive`/`Dead`. +#[derive(Copy, Clone)] +pub(super) struct MaybeStorageLive; + +impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { + type Domain = BitSet; + const NAME: &'static str = "maybe_storage_live"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + // bottom = dead + BitSet::new_empty(body.local_decls.len()) + } + + fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { + for arg in body.args_iter() { + state.insert(arg); + } + } +} + +impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { + type Idx = mir::Local; + + fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { + match stmt.kind { + mir::StatementKind::StorageLive(l) => trans.gen(l), + mir::StatementKind::StorageDead(l) => trans.kill(l), + _ => (), + } + } + + fn terminator_effect( + &self, + _trans: &mut impl GenKill, + _terminator: &mir::Terminator<'tcx>, + _loc: mir::Location, + ) { + } + + fn call_return_effect( + &self, + _trans: &mut impl GenKill, + _block: mir::BasicBlock, + _return_places: CallReturnPlaces<'_, 'tcx>, + ) { + // Nothing to do when a call returns successfully + } +} diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs new file mode 100644 index 000000000000..818e603f665e --- /dev/null +++ b/clippy_utils/src/mir/mod.rs @@ -0,0 +1,164 @@ +use rustc_hir::{Expr, HirId}; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::{ + traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, +}; +use rustc_middle::ty::TyCtxt; + +mod maybe_storage_live; + +mod possible_borrower; +pub use possible_borrower::PossibleBorrowerMap; + +mod possible_origin; + +mod transitive_relation; + +#[derive(Clone, Debug, Default)] +pub struct LocalUsage { + /// The locations where the local is used, if any. + pub local_use_locs: Vec, + /// The locations where the local is consumed or mutated, if any. + pub local_consume_or_mutate_locs: Vec, +} + +pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -> Option> { + let init = vec![ + LocalUsage { + local_use_locs: Vec::new(), + local_consume_or_mutate_locs: Vec::new(), + }; + locals.len() + ]; + + traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| { + // Give up on loops + if tdata.terminator().successors().any(|s| s == location.block) { + return None; + } + + let mut v = V { + locals, + location, + results: usage, + }; + v.visit_basic_block_data(tbb, tdata); + Some(v.results) + }) +} + +struct V<'a> { + locals: &'a [Local], + location: Location, + results: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for V<'a> { + fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) { + if loc.block == self.location.block && loc.statement_index <= self.location.statement_index { + return; + } + + let local = place.local; + + for (i, self_local) in self.locals.iter().enumerate() { + if local == *self_local { + if !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) { + self.results[i].local_use_locs.push(loc); + } + if matches!( + ctx, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) { + self.results[i].local_consume_or_mutate_locs.push(loc); + } + } + } + } +} + +/// Convenience wrapper around `visit_local_usage`. +pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option { + visit_local_usage( + &[local], + mir, + Location { + block: START_BLOCK, + statement_index: 0, + }, + ) + .map(|mut vec| { + let LocalUsage { local_use_locs, .. } = vec.remove(0); + local_use_locs + .into_iter() + .filter(|location| !is_local_assignment(mir, local, *location)) + .count() + == 1 + }) +} + +/// Returns the `mir::Body` containing the node associated with `hir_id`. +#[allow(clippy::module_name_repetitions)] +pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> { + let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id); + tcx.optimized_mir(body_owner_local_def_id.to_def_id()) +} + +/// Tries to determine the `Local` corresponding to `expr`, if any. +/// This function is expensive and should be used sparingly. +pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { + let mir = enclosing_mir(tcx, expr.hir_id); + mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| { + if local_decl.source_info.span == expr.span { + Some(local) + } else { + None + } + }) +} + +/// Returns a vector of `mir::Location` where `local` is assigned. +pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec { + let mut locations = Vec::new(); + for (block, data) in mir.basic_blocks.iter_enumerated() { + for statement_index in 0..=data.statements.len() { + let location = Location { block, statement_index }; + if is_local_assignment(mir, local, location) { + locations.push(location); + } + } + } + locations +} + +// `is_local_assignment` is based on `is_place_assignment`: +// https://github.com/rust-lang/rust/blob/b7413511dc85ec01ef4b91785f86614589ac6103/compiler/rustc_middle/src/mir/visit.rs#L1350 +fn is_local_assignment(mir: &Body<'_>, local: Local, location: Location) -> bool { + let Location { block, statement_index } = location; + let basic_block = &mir.basic_blocks[block]; + if statement_index < basic_block.statements.len() { + let statement = &basic_block.statements[statement_index]; + if let StatementKind::Assign(box (place, _)) = statement.kind { + place.as_local() == Some(local) + } else { + false + } + } else { + let terminator = basic_block.terminator(); + match &terminator.kind { + TerminatorKind::Call { destination, .. } => destination.as_local() == Some(local), + TerminatorKind::InlineAsm { operands, .. } => operands.iter().any(|operand| { + if let InlineAsmOperand::Out { place: Some(place), .. } = operand { + place.as_local() == Some(local) + } else { + false + } + }), + _ => false, + } + } +} diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs new file mode 100644 index 000000000000..25717bf3d2fe --- /dev/null +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -0,0 +1,241 @@ +use super::{ + maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor, + transitive_relation::TransitiveRelation, +}; +use crate::ty::is_copy; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::{BitSet, HybridBitSet}; +use rustc_lint::LateContext; +use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; +use rustc_middle::ty::{self, visit::TypeVisitor}; +use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use std::ops::ControlFlow; + +/// Collects the possible borrowers of each local. +/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` +/// possible borrowers of `a`. +#[allow(clippy::module_name_repetitions)] +struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { + possible_borrower: TransitiveRelation, + body: &'b mir::Body<'tcx>, + cx: &'a LateContext<'tcx>, + possible_origin: FxHashMap>, +} + +impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn new( + cx: &'a LateContext<'tcx>, + body: &'b mir::Body<'tcx>, + possible_origin: FxHashMap>, + ) -> Self { + Self { + possible_borrower: TransitiveRelation::default(), + cx, + body, + possible_origin, + } + } + + fn into_map( + self, + cx: &'a LateContext<'tcx>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + ) -> PossibleBorrowerMap<'b, 'tcx> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); + if !borrowers.is_empty() { + map.insert(row, borrowers); + } + } + + let bs = BitSet::new_empty(self.body.local_decls.len()); + PossibleBorrowerMap { + map, + maybe_live, + bitset: (bs.clone(), bs), + } + } +} + +impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + self.possible_borrower.add(borrowed.local, lhs); + }, + other => { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) + .is_continue() + { + return; + } + rvalue_locals(other, |rhs| { + if lhs != rhs { + self.possible_borrower.add(rhs, lhs); + } + }); + }, + } + } + + fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { + if let mir::TerminatorKind::Call { + args, + destination: mir::Place { local: dest, .. }, + .. + } = &terminator.kind + { + // TODO add doc + // If the call returns something with lifetimes, + // let's conservatively assume the returned value contains lifetime of all the arguments. + // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. + + let mut immutable_borrowers = vec![]; + let mut mutable_borrowers = vec![]; + + for op in args { + match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => { + if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() { + mutable_borrowers.push(p.local); + } else { + immutable_borrowers.push(p.local); + } + }, + mir::Operand::Constant(..) => (), + } + } + + let mut mutable_variables: Vec = mutable_borrowers + .iter() + .filter_map(|r| self.possible_origin.get(r)) + .flat_map(HybridBitSet::iter) + .collect(); + + if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() { + mutable_variables.push(*dest); + } + + for y in mutable_variables { + for x in &immutable_borrowers { + self.possible_borrower.add(*x, y); + } + for x in &mutable_borrowers { + self.possible_borrower.add(*x, y); + } + } + } + } +} + +struct ContainsRegion; + +impl TypeVisitor<'_> for ContainsRegion { + type BreakTy = (); + + fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow { + ControlFlow::BREAK + } +} + +fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { + use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use}; + + let mut visit_op = |op: &mir::Operand<'_>| match op { + mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), + mir::Operand::Constant(..) => (), + }; + + match rvalue { + Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op), + Aggregate(_, ops) => ops.iter().for_each(visit_op), + BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => { + visit_op(lhs); + visit_op(rhs); + }, + _ => (), + } +} + +/// Result of `PossibleBorrowerVisitor`. +#[allow(clippy::module_name_repetitions)] +pub struct PossibleBorrowerMap<'b, 'tcx> { + /// Mapping `Local -> its possible borrowers` + pub map: FxHashMap>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + // Caches to avoid allocation of `BitSet` on every query + pub bitset: (BitSet, BitSet), +} + +impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { + let possible_origin = { + let mut vis = PossibleOriginVisitor::new(mir); + vis.visit_body(mir); + vis.into_map(cx) + }; + let maybe_storage_live_result = MaybeStorageLive + .into_engine(cx.tcx, mir) + .pass_name("redundant_clone") + .iterate_to_fixpoint() + .into_results_cursor(mir); + let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); + vis.visit_body(mir); + vis.into_map(cx, maybe_storage_live_result) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. + pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { + self.bounded_borrowers(borrowers, borrowers, borrowed, at) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` + /// but no more than `above`. + pub fn bounded_borrowers( + &mut self, + below: &[mir::Local], + above: &[mir::Local], + borrowed: mir::Local, + at: mir::Location, + ) -> bool { + self.maybe_live.seek_after_primary_effect(at); + + self.bitset.0.clear(); + let maybe_live = &mut self.maybe_live; + if let Some(bitset) = self.map.get(&borrowed) { + for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { + self.bitset.0.insert(b); + } + } else { + return false; + } + + self.bitset.1.clear(); + for b in below { + self.bitset.1.insert(*b); + } + + if !self.bitset.0.superset(&self.bitset.1) { + return false; + } + + for b in above { + self.bitset.0.remove(*b); + } + + self.bitset.0.is_empty() + } + + pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + self.maybe_live.seek_after_primary_effect(at); + self.maybe_live.contains(local) + } +} diff --git a/clippy_utils/src/mir/possible_origin.rs b/clippy_utils/src/mir/possible_origin.rs new file mode 100644 index 000000000000..8e7513d740ab --- /dev/null +++ b/clippy_utils/src/mir/possible_origin.rs @@ -0,0 +1,59 @@ +use super::transitive_relation::TransitiveRelation; +use crate::ty::is_copy; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::HybridBitSet; +use rustc_lint::LateContext; +use rustc_middle::mir; + +/// Collect possible borrowed for every `&mut` local. +/// For example, `_1 = &mut _2` generate _1: {_2,...} +/// Known Problems: not sure all borrowed are tracked +#[allow(clippy::module_name_repetitions)] +pub(super) struct PossibleOriginVisitor<'a, 'tcx> { + possible_origin: TransitiveRelation, + body: &'a mir::Body<'tcx>, +} + +impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> { + pub fn new(body: &'a mir::Body<'tcx>) -> Self { + Self { + possible_origin: TransitiveRelation::default(), + body, + } + } + + pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } + + let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); + if !borrowers.is_empty() { + map.insert(row, borrowers); + } + } + map + } +} + +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + // Only consider `&mut`, which can modify origin place + mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) | + // _2: &mut _; + // _3 = move _2 + mir::Rvalue::Use(mir::Operand::Move(borrowed)) | + // _3 = move _2 as &mut _; + mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _) + => { + self.possible_origin.add(lhs, borrowed.local); + }, + _ => {}, + } + } +} diff --git a/clippy_utils/src/mir/transitive_relation.rs b/clippy_utils/src/mir/transitive_relation.rs new file mode 100644 index 000000000000..7fe2960739fa --- /dev/null +++ b/clippy_utils/src/mir/transitive_relation.rs @@ -0,0 +1,29 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_index::bit_set::HybridBitSet; +use rustc_middle::mir; + +#[derive(Default)] +pub(super) struct TransitiveRelation { + relations: FxHashMap>, +} + +impl TransitiveRelation { + pub fn add(&mut self, a: mir::Local, b: mir::Local) { + self.relations.entry(a).or_default().push(b); + } + + pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet { + let mut seen = HybridBitSet::new_empty(domain_size); + let mut stack = vec![a]; + while let Some(u) = stack.pop() { + if let Some(edges) = self.relations.get(&u) { + for &v in edges { + if seen.insert(v) { + stack.push(v); + } + } + } + } + seen + } +} diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 80098d9766c6..c5dcd7b31f58 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -69,12 +69,13 @@ impl<'a> NumericLiteral<'a> { #[must_use] pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self { + let unsigned_lit = lit.trim_start_matches('-'); // Determine delimiter for radix prefix, if present, and radix. - let radix = if lit.starts_with("0x") { + let radix = if unsigned_lit.starts_with("0x") { Radix::Hexadecimal - } else if lit.starts_with("0b") { + } else if unsigned_lit.starts_with("0b") { Radix::Binary - } else if lit.starts_with("0o") { + } else if unsigned_lit.starts_with("0o") { Radix::Octal } else { Radix::Decimal diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 13938645fc3e..bc8514734304 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -16,25 +16,17 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ #[cfg(feature = "internal")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; -pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; -pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; -pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; -pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; -pub const CORE_ITER_INTO_ITER: [&str; 6] = ["core", "iter", "traits", "collect", "IntoIterator", "into_iter"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; -/// Preferably use the diagnostic item `sym::deref_method` where possible -pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; -pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; #[cfg(feature = "internal")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; #[cfg(feature = "internal")] @@ -42,30 +34,22 @@ pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"] pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; -pub const FILE: [&str; 3] = ["std", "fs", "File"]; -pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; -pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; -pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; -pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"]; #[cfg(feature = "internal")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; #[cfg(feature = "internal")] pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; -pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; -pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; -pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -76,13 +60,7 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; #[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"]; -pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; -/// Preferably use the diagnostic item `sym::Option` where possible -pub const OPTION: [&str; 3] = ["core", "option", "Option"]; -pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"]; -pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"]; -pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; @@ -95,8 +73,6 @@ pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; #[cfg_attr(not(unix), allow(clippy::invalid_paths))] pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"]; pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; -pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; -pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"]; pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; @@ -119,26 +95,14 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; -/// Preferably use the diagnostic item `sym::Result` where possible -pub const RESULT: [&str; 3] = ["core", "result", "Result"]; -pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; -pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; #[cfg(feature = "internal")] pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"]; -pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"]; -pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 5089987ef720..aad7da61a8a5 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1,7 +1,9 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite}; +use crate::source::{ + snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite, +}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; use rustc_ast::util::parser::AssocOp; @@ -110,7 +112,7 @@ impl<'a> Sugg<'a> { if expr.span.ctxt() == ctxt { Self::hir_from_snippet(expr, |span| snippet(cx, span, default)) } else { - let snip = snippet_with_applicability(cx, expr.span, default, applicability); + let (snip, _) = snippet_with_context(cx, expr.span, ctxt, default, applicability); Sugg::NonParen(snip) } } @@ -1052,12 +1054,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read( - &mut self, - _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) {} + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } #[cfg(test)] diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index b344db634f61..b8824024e6c7 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -73,8 +73,7 @@ impl LintcheckConfig { let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { clap_config .get_one::("crates-toml") - .map(|s| &**s) - .unwrap_or("lintcheck/lintcheck_crates.toml") + .map_or("lintcheck/lintcheck_crates.toml", |s| &**s) .into() }); @@ -97,7 +96,7 @@ impl LintcheckConfig { Some(&0) => { // automatic choice // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize + rayon::current_num_threads() / 2 }, Some(&threads) => threads, // no -j passed, use a single thread diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs index 63221bab32d3..47724a2fedb0 100644 --- a/lintcheck/src/driver.rs +++ b/lintcheck/src/driver.rs @@ -5,7 +5,7 @@ use std::net::TcpStream; use std::process::{self, Command, Stdio}; use std::{env, mem}; -/// 1. Sends [DriverInfo] to the [crate::recursive::LintcheckServer] running on `addr` +/// 1. Sends [`DriverInfo`] to the [`crate::recursive::LintcheckServer`] running on `addr` /// 2. Receives [bool] from the server, if `false` returns `None` /// 3. Otherwise sends the stderr of running `clippy-driver` to the server fn run_clippy(addr: &str) -> Option { diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index cc2b3e1acec7..54c1b80c42db 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -116,12 +116,13 @@ impl ClippyWarning { let span = diag.spans.into_iter().find(|span| span.is_primary)?; - let file = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { - Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), - Err(_) => format!( + let file = if let Ok(stripped) = Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { + format!("$CARGO_HOME/{}", stripped.display()) + } else { + format!( "target/lintcheck/sources/{}-{}/{}", crate_name, crate_version, span.file_name - ), + ) }; Some(Self { @@ -144,16 +145,17 @@ impl ClippyWarning { } let mut output = String::from("| "); - let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); + let _ = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line); let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); output.push('\n'); output } else { - format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message) + format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.message) } } } +#[allow(clippy::result_large_err)] fn get(path: &str) -> Result { const MAX_RETRIES: u8 = 4; let mut retries = 0; @@ -161,11 +163,11 @@ fn get(path: &str) -> Result { match ureq::get(path).call() { Ok(res) => return Ok(res), Err(e) if retries >= MAX_RETRIES => return Err(e), - Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e), + Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"), Err(e) => return Err(e), } - eprintln!("retrying in {} seconds...", retries); - thread::sleep(Duration::from_secs(retries as u64)); + eprintln!("retrying in {retries} seconds..."); + thread::sleep(Duration::from_secs(u64::from(retries))); retries += 1; } } @@ -181,11 +183,11 @@ impl CrateSource { let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); // url to download the crate from crates.io - let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version); - println!("Downloading and extracting {} {} from {}", name, version, url); + let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download"); + println!("Downloading and extracting {name} {version} from {url}"); create_dirs(&krate_download_dir, &extract_dir); - let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version)); + let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz")); // don't download/extract if we already have done so if !krate_file_path.is_file() { // create a file path to download and write the crate data into @@ -205,7 +207,7 @@ impl CrateSource { Crate { version: version.clone(), name: name.clone(), - path: extract_dir.join(format!("{}-{}/", name, version)), + path: extract_dir.join(format!("{name}-{version}/")), options: options.clone(), } }, @@ -218,12 +220,12 @@ impl CrateSource { let repo_path = { let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); // add a -git suffix in case we have the same crate from crates.io and a git repo - repo_path.push(format!("{}-git", name)); + repo_path.push(format!("{name}-git")); repo_path }; // clone the repo if we have not done so if !repo_path.is_dir() { - println!("Cloning {} and checking out {}", url, commit); + println!("Cloning {url} and checking out {commit}"); if !Command::new("git") .arg("clone") .arg(url) @@ -232,7 +234,7 @@ impl CrateSource { .expect("Failed to clone git repo!") .success() { - eprintln!("Failed to clone {} into {}", url, repo_path.display()) + eprintln!("Failed to clone {url} into {}", repo_path.display()); } } // check out the commit/branch/whatever @@ -245,7 +247,7 @@ impl CrateSource { .expect("Failed to check out commit") .success() { - eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display()) + eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()); } Crate { @@ -256,22 +258,22 @@ impl CrateSource { } }, CrateSource::Path { name, path, options } => { + fn is_cache_dir(entry: &DirEntry) -> bool { + std::fs::read(entry.path().join("CACHEDIR.TAG")) + .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) + .unwrap_or(false) + } + // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file. // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory // as a result of this filter. let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); if dest_crate_root.exists() { - println!("Deleting existing directory at {:?}", dest_crate_root); + println!("Deleting existing directory at {dest_crate_root:?}"); std::fs::remove_dir_all(&dest_crate_root).unwrap(); } - println!("Copying {:?} to {:?}", path, dest_crate_root); - - fn is_cache_dir(entry: &DirEntry) -> bool { - std::fs::read(entry.path().join("CACHEDIR.TAG")) - .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) - .unwrap_or(false) - } + println!("Copying {path:?} to {dest_crate_root:?}"); for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) { let entry = entry.unwrap(); @@ -301,6 +303,7 @@ impl CrateSource { impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued + #[allow(clippy::too_many_arguments)] fn run_clippy_lints( &self, cargo_clippy_path: &Path, @@ -345,14 +348,14 @@ impl Crate { clippy_args.push(opt); } } else { - clippy_args.extend(&["-Wclippy::pedantic", "-Wclippy::cargo"]) + clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]); } if lint_filter.is_empty() { clippy_args.push("--cap-lints=warn"); } else { clippy_args.push("--cap-lints=allow"); - clippy_args.extend(lint_filter.iter().map(|filter| filter.as_str())) + clippy_args.extend(lint_filter.iter().map(std::string::String::as_str)); } if let Some(server) = server { @@ -389,10 +392,7 @@ impl Crate { let all_output = Command::new(&cargo_clippy_path) // use the looping index to create individual target dirs - .env( - "CARGO_TARGET_DIR", - shared_target_dir.join(format!("_{:?}", thread_index)), - ) + .env("CARGO_TARGET_DIR", shared_target_dir.join(format!("_{thread_index:?}"))) .args(&cargo_clippy_args) .current_dir(&self.path) .output() @@ -422,8 +422,8 @@ impl Crate { { let subcrate = &stderr[63..]; println!( - "ERROR: failed to apply some suggetion to {} / to (sub)crate {}", - self.name, subcrate + "ERROR: failed to apply some suggetion to {} / to (sub)crate {subcrate}", + self.name ); } // fast path, we don't need the warnings anyway @@ -457,20 +457,16 @@ fn build_clippy() { /// Read a `lintcheck_crates.toml` file fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = - std::fs::read_to_string(&toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); + std::fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = - toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e)); + toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display())); // parse the hashmap of the toml file into a list of crates - let tomlcrates: Vec = crate_list - .crates - .into_iter() - .map(|(_cratename, tomlcrate)| tomlcrate) - .collect(); + let tomlcrates: Vec = crate_list.crates.into_values().collect(); // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => // multiple Cratesources) let mut crate_sources = Vec::new(); - tomlcrates.into_iter().for_each(|tk| { + for tk in tomlcrates { if let Some(ref path) = tk.path { crate_sources.push(CrateSource::Path { name: tk.name.clone(), @@ -479,13 +475,13 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { }); } else if let Some(ref versions) = tk.versions { // if we have multiple versions, save each one - versions.iter().for_each(|ver| { + for ver in versions.iter() { crate_sources.push(CrateSource::CratesIo { name: tk.name.clone(), version: ver.to_string(), options: tk.options.clone(), }); - }) + } } else if tk.git_url.is_some() && tk.git_hash.is_some() { // otherwise, we should have a git source crate_sources.push(CrateSource::Git { @@ -502,16 +498,19 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) || tk.git_hash.is_some() != tk.git_url.is_some() { - eprintln!("tomlkrate: {:?}", tk); - if tk.git_hash.is_some() != tk.git_url.is_some() { - panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!"); - } - if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) { - panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"); - } + eprintln!("tomlkrate: {tk:?}"); + assert_eq!( + tk.git_hash.is_some(), + tk.git_url.is_some(), + "Error: Encountered TomlCrate with only one of git_hash and git_url!" + ); + assert!( + tk.path.is_none() || (tk.git_hash.is_none() && tk.versions.is_none()), + "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields" + ); unreachable!("Failed to translate TomlCrate into CrateSource!"); } - }); + } // sort the crates crate_sources.sort(); @@ -530,13 +529,13 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect(); // sort by "000{count} {clippy::lintname}" // to not have a lint with 200 and 2 warnings take the same spot - stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint)); + stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}")); let mut header = String::from("| lint | count |\n"); header.push_str("| -------------------------------------------------- | ----- |\n"); let stats_string = stats .iter() - .map(|(lint, count)| format!("| {:<50} | {:>4} |\n", lint, count)) + .map(|(lint, count)| format!("| {lint:<50} | {count:>4} |\n")) .fold(header, |mut table, line| { table.push_str(&line); table @@ -573,6 +572,7 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool logs_modified < clippy_modified } +#[allow(clippy::too_many_lines)] fn main() { // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` if let Ok(addr) = env::var("LINTCHECK_SERVER") { @@ -602,10 +602,10 @@ fn main() { ) { let shared_target_dir = "target/lintcheck/shared_target_dir"; // if we get an Err here, the shared target dir probably does simply not exist - if let Ok(metadata) = std::fs::metadata(&shared_target_dir) { + if let Ok(metadata) = std::fs::metadata(shared_target_dir) { if metadata.is_dir() { println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir..."); - std::fs::remove_dir_all(&shared_target_dir) + std::fs::remove_dir_all(shared_target_dir) .expect("failed to remove target/lintcheck/shared_target_dir"); } } @@ -678,7 +678,7 @@ fn main() { .unwrap(); let server = config.recursive.then(|| { - let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); + fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive").unwrap_or_default(); LintcheckServer::spawn(recursive_options) }); @@ -734,8 +734,8 @@ fn main() { } write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); - for (cratename, msg) in ices.iter() { - let _ = write!(text, "{}: '{}'", cratename, msg); + for (cratename, msg) in &ices { + let _ = write!(text, "{cratename}: '{msg}'"); } println!("Writing logs to {}", config.lintcheck_results_path.display()); @@ -779,7 +779,7 @@ fn read_stats_from_file(file_path: &Path) -> HashMap { fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, usize>, lint_filter: &Vec) { let same_in_both_hashmaps = old_stats .iter() - .filter(|(old_key, old_val)| new_stats.get::<&String>(&old_key) == Some(old_val)) + .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val)) .map(|(k, v)| (k.to_string(), *v)) .collect::>(); @@ -787,37 +787,37 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us let mut new_stats_deduped = new_stats; // remove duplicates from both hashmaps - same_in_both_hashmaps.iter().for_each(|(k, v)| { + for (k, v) in &same_in_both_hashmaps { assert!(old_stats_deduped.remove(k) == Some(*v)); assert!(new_stats_deduped.remove(k) == Some(*v)); - }); + } println!("\nStats:"); // list all new counts (key is in new stats but not in old stats) new_stats_deduped .iter() - .filter(|(new_key, _)| old_stats_deduped.get::(&new_key).is_none()) + .filter(|(new_key, _)| old_stats_deduped.get::(new_key).is_none()) .for_each(|(new_key, new_value)| { - println!("{} 0 => {}", new_key, new_value); + println!("{new_key} 0 => {new_value}"); }); // list all changed counts (key is in both maps but value differs) new_stats_deduped .iter() - .filter(|(new_key, _new_val)| old_stats_deduped.get::(&new_key).is_some()) + .filter(|(new_key, _new_val)| old_stats_deduped.get::(new_key).is_some()) .for_each(|(new_key, new_val)| { - let old_val = old_stats_deduped.get::(&new_key).unwrap(); - println!("{} {} => {}", new_key, old_val, new_val); + let old_val = old_stats_deduped.get::(new_key).unwrap(); + println!("{new_key} {old_val} => {new_val}"); }); // list all gone counts (key is in old status but not in new stats) old_stats_deduped .iter() - .filter(|(old_key, _)| new_stats_deduped.get::<&String>(&old_key).is_none()) + .filter(|(old_key, _)| new_stats_deduped.get::<&String>(old_key).is_none()) .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key)) .for_each(|(old_key, old_value)| { - println!("{} {} => 0", old_key, old_value); + println!("{old_key} {old_value} => 0"); }); } @@ -828,19 +828,21 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us /// This function panics if creating one of the dirs fails. fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create lintcheck target dir"); - } + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create lintcheck target dir" + ); }); - std::fs::create_dir(&krate_download_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate download dir"); - } + std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| { + assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir"); }); - std::fs::create_dir(&extract_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate extraction dir"); - } + std::fs::create_dir(extract_dir).unwrap_or_else(|err| { + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create crate extraction dir" + ); }); } @@ -863,7 +865,7 @@ fn lintcheck_test() { "lintcheck/test_sources.toml", ]; let status = std::process::Command::new("cargo") - .args(&args) + .args(args) .current_dir("..") // repo root .status(); //.output(); diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs index 67dcfc2b199c..49072e65192f 100644 --- a/lintcheck/src/recursive.rs +++ b/lintcheck/src/recursive.rs @@ -1,7 +1,7 @@ //! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`, -//! this allows [crate::driver] to be run for every dependency. The driver connects to -//! [LintcheckServer] to ask if it should be skipped, and if not sends the stderr of running clippy -//! on the crate to the server +//! this allows [`crate::driver`] to be run for every dependency. The driver connects to +//! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running +//! clippy on the crate to the server use crate::ClippyWarning; use crate::RecursiveOptions; @@ -109,8 +109,8 @@ impl LintcheckServer { Self { local_addr, - sender, receiver, + sender, } } diff --git a/rust-toolchain b/rust-toolchain index 49b13cb54e71..748d8a317160 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-10-06" +channel = "nightly-2022-10-20" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/docs.rs b/src/docs.rs index bd27bc7938f8..c033ad294a38 100644 --- a/src/docs.rs +++ b/src/docs.rs @@ -28,6 +28,7 @@ docs! { "approx_constant", "arithmetic_side_effects", "as_conversions", + "as_ptr_cast_mut", "as_underscore", "assertions_on_constants", "assertions_on_result_states", @@ -60,6 +61,7 @@ docs! { "cast_enum_constructor", "cast_enum_truncation", "cast_lossless", + "cast_nan_to_int", "cast_possible_truncation", "cast_possible_wrap", "cast_precision_loss", @@ -257,6 +259,7 @@ docs! { "manual_async_fn", "manual_bits", "manual_clamp", + "manual_filter", "manual_filter_map", "manual_find", "manual_find_map", @@ -313,6 +316,7 @@ docs! { "missing_panics_doc", "missing_safety_doc", "missing_spin_loop", + "missing_trait_methods", "mistyped_literal_suffixes", "mixed_case_hex_literals", "mixed_read_write_in_expression", @@ -391,6 +395,7 @@ docs! { "panic", "panic_in_result_fn", "panicking_unwrap", + "partial_pub_fields", "partialeq_ne_impl", "partialeq_to_none", "path_buf_push_overwrite", @@ -553,6 +558,7 @@ docs! { "unseparated_literal_suffix", "unsound_collection_transmute", "unused_async", + "unused_format_specs", "unused_io_amount", "unused_peekable", "unused_rounding", diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt new file mode 100644 index 000000000000..228dde996bb2 --- /dev/null +++ b/src/docs/as_ptr_cast_mut.txt @@ -0,0 +1,19 @@ +### What it does +Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer + +### Why is this bad? +Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior +mutability is used, making it unlikely that having it as a mutable pointer is correct. + +### Example +``` +let string = String::with_capacity(1); +let ptr = string.as_ptr() as *mut u8; +unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR +``` +Use instead: +``` +let mut string = String::with_capacity(1); +let ptr = string.as_mut_ptr(); +unsafe { ptr.write(4) }; +``` \ No newline at end of file diff --git a/src/docs/box_default.txt b/src/docs/box_default.txt index ffac894d0c50..1c670c773337 100644 --- a/src/docs/box_default.txt +++ b/src/docs/box_default.txt @@ -7,12 +7,6 @@ First, it's more complex, involving two calls instead of one. Second, `Box::default()` can be faster [in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). -### Known problems -The lint may miss some cases (e.g. Box::new(String::from(""))). -On the other hand, it will trigger on cases where the `default` -code comes from a macro that does something different based on -e.g. target operating system. - ### Example ``` let x: Box = Box::new(Default::default()); diff --git a/src/docs/cast_nan_to_int.txt b/src/docs/cast_nan_to_int.txt new file mode 100644 index 000000000000..122f5da0c921 --- /dev/null +++ b/src/docs/cast_nan_to_int.txt @@ -0,0 +1,15 @@ +### What it does +Checks for a known NaN float being cast to an integer + +### Why is this bad? +NaNs are cast into zero, so one could simply use this and make the +code more readable. The lint could also hint at a programmer error. + +### Example +``` +let _: (0.0_f32 / 0.0) as u64; +``` +Use instead: +``` +let _: = 0_u64; +``` \ No newline at end of file diff --git a/src/docs/manual_filter.txt b/src/docs/manual_filter.txt new file mode 100644 index 000000000000..19a4d9319d94 --- /dev/null +++ b/src/docs/manual_filter.txt @@ -0,0 +1,21 @@ +### What it does +Checks for usages of `match` which could be implemented using `filter` + +### Why is this bad? +Using the `filter` method is clearer and more concise. + +### Example +``` +match Some(0) { + Some(x) => if x % 2 == 0 { + Some(x) + } else { + None + }, + None => None, +}; +``` +Use instead: +``` +Some(0).filter(|&x| x % 2 == 0); +``` \ No newline at end of file diff --git a/src/docs/missing_trait_methods.txt b/src/docs/missing_trait_methods.txt new file mode 100644 index 000000000000..788ad764f8c3 --- /dev/null +++ b/src/docs/missing_trait_methods.txt @@ -0,0 +1,40 @@ +### What it does +Checks if a provided method is used implicitly by a trait +implementation. A usage example would be a wrapper where every method +should perform some operation before delegating to the inner type's +implemenation. + +This lint should typically be enabled on a specific trait `impl` item +rather than globally. + +### Why is this bad? +Indicates that a method is missing. + +### Example +``` +trait Trait { + fn required(); + + fn provided() {} +} + +#[warn(clippy::missing_trait_methods)] +impl Trait for Type { + fn required() { /* ... */ } +} +``` +Use instead: +``` +trait Trait { + fn required(); + + fn provided() {} +} + +#[warn(clippy::missing_trait_methods)] +impl Trait for Type { + fn required() { /* ... */ } + + fn provided() { /* ... */ } +} +``` \ No newline at end of file diff --git a/src/docs/partial_pub_fields.txt b/src/docs/partial_pub_fields.txt new file mode 100644 index 000000000000..b529adf1547d --- /dev/null +++ b/src/docs/partial_pub_fields.txt @@ -0,0 +1,27 @@ +### What it does +Checks whether partial fields of a struct are public. + +Either make all fields of a type public, or make none of them public + +### Why is this bad? +Most types should either be: +* Abstract data types: complex objects with opaque implementation which guard +interior invariants and expose intentionally limited API to the outside world. +* Data: relatively simple objects which group a bunch of related attributes together. + +### Example +``` +pub struct Color { + pub r: u8, + pub g: u8, + b: u8, +} +``` +Use instead: +``` +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, +} +``` \ No newline at end of file diff --git a/src/docs/unused_format_specs.txt b/src/docs/unused_format_specs.txt new file mode 100644 index 000000000000..77be3a2fb170 --- /dev/null +++ b/src/docs/unused_format_specs.txt @@ -0,0 +1,24 @@ +### What it does +Detects [formatting parameters] that have no effect on the output of +`format!()`, `println!()` or similar macros. + +### Why is this bad? +Shorter format specifiers are easier to read, it may also indicate that +an expected formatting operation such as adding padding isn't happening. + +### Example +``` +println!("{:.}", 1.0); + +println!("not padded: {:5}", format_args!("...")); +``` +Use instead: +``` +println!("{}", 1.0); + +println!("not padded: {}", format_args!("...")); +// OR +println!("padded: {:5}", format!("...")); +``` + +[formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters \ No newline at end of file diff --git a/tests/compile-test.rs b/tests/compile-test.rs index fa769222d1af..c10ee969c014 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -283,7 +283,7 @@ fn run_ui_cargo() { env::set_current_dir(&src_path)?; let cargo_toml_path = case.path().join("Cargo.toml"); - let cargo_content = fs::read(&cargo_toml_path)?; + let cargo_content = fs::read(cargo_toml_path)?; let cargo_parsed: toml::Value = toml::from_str( std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"), ) diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 961525bbd910..6d0022f7a5cc 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -20,7 +20,14 @@ fn dogfood_clippy() { } // "" is the root package - for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { + for package in &[ + "", + "clippy_dev", + "clippy_lints", + "clippy_utils", + "lintcheck", + "rustc_tools_util", + ] { run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); } } diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index a1b8e2ee162c..07c5941013c1 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,4 +1,4 @@ -thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs +thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs index b823ff7fe37f..9a9790a4bae5 100644 --- a/tests/ui-internal/invalid_paths.rs +++ b/tests/ui-internal/invalid_paths.rs @@ -1,5 +1,5 @@ #![warn(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)] mod paths { // Good path diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index 4c050332f2cc..cbbb46523064 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -28,9 +28,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { @@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = is_type_diagnostic_item(cx, ty, sym::Result); let _ = is_type_diagnostic_item(cx, ty, sym::Result); - #[allow(unused)] + #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = is_type_diagnostic_item(cx, ty, sym::Rc); diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index 6506f1f164ac..f17fed6c6530 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -28,9 +28,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { @@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = match_type(cx, ty, RESULT); let _ = match_type(cx, ty, &["core", "result", "Result"]); - #[allow(unused)] + #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = clippy_utils::ty::match_type(cx, ty, rc_path); diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs new file mode 100644 index 000000000000..b5ff3a542056 --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs @@ -0,0 +1,16 @@ +#![feature(rustc_private)] +#![allow(unused)] +#![warn(clippy::unnecessary_def_path)] + +extern crate rustc_hir; + +use rustc_hir::LangItem; + +fn main() { + const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + + // Don't lint, not yet a diagnostic or language item + const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; +} diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr new file mode 100644 index 000000000000..af46d87bf676 --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -0,0 +1,27 @@ +error: hardcoded path to a language item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 + | +LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `LangItem::DerefMut` + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 + | +LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::deref_method` + +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 + | +LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::Deref` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/as_ptr_cast_mut.rs b/tests/ui/as_ptr_cast_mut.rs new file mode 100644 index 000000000000..0d1d9258433b --- /dev/null +++ b/tests/ui/as_ptr_cast_mut.rs @@ -0,0 +1,37 @@ +#![allow(unused)] +#![warn(clippy::as_ptr_cast_mut)] +#![allow(clippy::wrong_self_convention)] + +struct MutPtrWrapper(Vec); +impl MutPtrWrapper { + fn as_ptr(&mut self) -> *const u8 { + self.0.as_mut_ptr() as *const u8 + } +} + +struct Covariant(*const T); +impl Covariant { + fn as_ptr(self) -> *const T { + self.0 + } +} + +fn main() { + let mut string = String::new(); + let _ = string.as_ptr() as *mut u8; + let _: *mut i8 = string.as_ptr() as *mut _; + let _ = string.as_ptr() as *const i8; + let _ = string.as_mut_ptr(); + let _ = string.as_mut_ptr() as *mut u8; + let _ = string.as_mut_ptr() as *const u8; + + let nn = std::ptr::NonNull::new(4 as *mut u8).unwrap(); + let _ = nn.as_ptr() as *mut u8; + + let mut wrap = MutPtrWrapper(Vec::new()); + let _ = wrap.as_ptr() as *mut u8; + + let mut local = 4; + let ref_with_write_perm = Covariant(std::ptr::addr_of_mut!(local) as *const _); + let _ = ref_with_write_perm.as_ptr() as *mut u8; +} diff --git a/tests/ui/as_ptr_cast_mut.stderr b/tests/ui/as_ptr_cast_mut.stderr new file mode 100644 index 000000000000..2189c3d2f855 --- /dev/null +++ b/tests/ui/as_ptr_cast_mut.stderr @@ -0,0 +1,16 @@ +error: casting the result of `as_ptr` to *mut u8 + --> $DIR/as_ptr_cast_mut.rs:21:13 + | +LL | let _ = string.as_ptr() as *mut u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()` + | + = note: `-D clippy::as-ptr-cast-mut` implied by `-D warnings` + +error: casting the result of `as_ptr` to *mut i8 + --> $DIR/as_ptr_cast_mut.rs:22:22 + | +LL | let _: *mut i8 = string.as_ptr() as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/author.stdout b/tests/ui/author.stdout index 597318a556b8..27ad538f24d8 100644 --- a/tests/ui/author.stdout +++ b/tests/ui/author.stdout @@ -1,14 +1,12 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Cast(expr, cast_ty) = init.kind; - if let TyKind::Path(ref qpath) = cast_ty.kind; - if match_qpath(qpath, &["char"]); - if let ExprKind::Lit(ref lit) = expr.kind; - if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "x"; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Cast(expr, cast_ty) = init.kind + && let TyKind::Path(ref qpath) = cast_ty.kind + && match_qpath(qpath, &["char"]) + && let ExprKind::Lit(ref lit) = expr.kind + && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" +{ + // report your lint here } diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index a529981e2e68..9de0550d81d0 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -1,64 +1,58 @@ -if_chain! { - if let ExprKind::Block(block, None) = expr.kind; - if block.stmts.len() == 3; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Lit(ref lit) = init.kind; - if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "x"; - if let StmtKind::Local(local1) = block.stmts[1].kind; - if let Some(init1) = local1.init; - if let ExprKind::Lit(ref lit1) = init1.kind; - if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind; - if name1.as_str() == "_t"; - if let StmtKind::Semi(e) = block.stmts[2].kind; - if let ExprKind::Unary(UnOp::Neg, inner) = e.kind; - if let ExprKind::Path(ref qpath) = inner.kind; - if match_qpath(qpath, &["x"]); - if block.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 3 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Lit(ref lit) = init.kind + && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" + && let StmtKind::Local(local1) = block.stmts[1].kind + && let Some(init1) = local1.init + && let ExprKind::Lit(ref lit1) = init1.kind + && let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local1.pat.kind + && name1.as_str() == "_t" + && let StmtKind::Semi(e) = block.stmts[2].kind + && let ExprKind::Unary(UnOp::Neg, inner) = e.kind + && let ExprKind::Path(ref qpath) = inner.kind + && match_qpath(qpath, &["x"]) + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::Block(block, None) = expr.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["String", "new"]); - if args.is_empty(); - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind; - if name.as_str() == "expr"; - if let Some(trailing_expr) = block.expr; - if let ExprKind::Call(func1, args1) = trailing_expr.kind; - if let ExprKind::Path(ref qpath1) = func1.kind; - if match_qpath(qpath1, &["drop"]); - if args1.len() == 1; - if let ExprKind::Path(ref qpath2) = args1[0].kind; - if match_qpath(qpath2, &["expr"]); - then { - // report your lint here - } +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["String", "new"]) + && args.is_empty() + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local.pat.kind + && name.as_str() == "expr" + && let Some(trailing_expr) = block.expr + && let ExprKind::Call(func1, args1) = trailing_expr.kind + && let ExprKind::Path(ref qpath1) = func1.kind + && match_qpath(qpath1, &["drop"]) + && args1.len() == 1 + && let ExprKind::Path(ref qpath2) = args1[0].kind + && match_qpath(qpath2, &["expr"]) +{ + // report your lint here } -if_chain! { - if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind; - if let FnRetTy::DefaultReturn(_) = fn_decl.output; - let expr1 = &cx.tcx.hir().body(body_id).value; - if let ExprKind::Call(func, args) = expr1.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)); - if args.len() == 1; - if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind; - if let FnRetTy::DefaultReturn(_) = fn_decl1.output; - let expr2 = &cx.tcx.hir().body(body_id1).value; - if let ExprKind::Block(block, None) = expr2.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind + && let FnRetTy::DefaultReturn(_) = fn_decl.output + && expr1 = &cx.tcx.hir().body(body_id).value + && let ExprKind::Call(func, args) = expr1.kind + && let ExprKind::Path(ref qpath) = func.kind + && matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)) + && args.len() == 1 + && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind + && let FnRetTy::DefaultReturn(_) = fn_decl1.output + && expr2 = &cx.tcx.hir().body(body_id1).value + && let ExprKind::Block(block, None) = expr2.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout index 266312d63e50..f040f6330a64 100644 --- a/tests/ui/author/call.stdout +++ b/tests/ui/author/call.stdout @@ -1,16 +1,14 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]); - if args.len() == 2; - if let ExprKind::Lit(ref lit) = args[0].kind; - if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node; - if let ExprKind::Lit(ref lit1) = args[1].kind; - if let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node; - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]) + && args.len() == 2 + && let ExprKind::Lit(ref lit) = args[0].kind + && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node + && let ExprKind::Lit(ref lit1) = args[1].kind + && let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout index 8d92849b3668..5d79618820d8 100644 --- a/tests/ui/author/if.stdout +++ b/tests/ui/author/if.stdout @@ -1,50 +1,46 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::If(cond, then, Some(else_expr)) = init.kind; - if let ExprKind::DropTemps(expr) = cond.kind; - if let ExprKind::Lit(ref lit) = expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Binary(op, left, right) = e.kind; - if BinOpKind::Eq == op.node; - if let ExprKind::Lit(ref lit1) = left.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Lit(ref lit2) = right.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node; - if block.expr.is_none(); - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.len() == 1; - if let StmtKind::Semi(e1) = block1.stmts[0].kind; - if let ExprKind::Binary(op1, left1, right1) = e1.kind; - if BinOpKind::Eq == op1.node; - if let ExprKind::Lit(ref lit3) = left1.kind; - if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node; - if let ExprKind::Lit(ref lit4) = right1.kind; - if let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node; - if block1.expr.is_none(); - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::If(cond, then, Some(else_expr)) = init.kind + && let ExprKind::DropTemps(expr) = cond.kind + && let ExprKind::Lit(ref lit) = expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Block(block, None) = then.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Binary(op, left, right) = e.kind + && BinOpKind::Eq == op.node + && let ExprKind::Lit(ref lit1) = left.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Lit(ref lit2) = right.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node + && block.expr.is_none() + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.len() == 1 + && let StmtKind::Semi(e1) = block1.stmts[0].kind + && let ExprKind::Binary(op1, left1, right1) = e1.kind + && BinOpKind::Eq == op1.node + && let ExprKind::Lit(ref lit3) = left1.kind + && let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node + && let ExprKind::Lit(ref lit4) = right1.kind + && let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node + && block1.expr.is_none() + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } -if_chain! { - if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind; - if let ExprKind::Let(let_expr) = cond.kind; - if let PatKind::Lit(lit_expr) = let_expr.pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Path(ref qpath) = let_expr.init.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.is_empty(); - if block1.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind + && let ExprKind::Let(let_expr) = cond.kind + && let PatKind::Lit(lit_expr) = let_expr.pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Path(ref qpath) = let_expr.init.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = then.kind + && block.stmts.is_empty() + && block.expr.is_none() + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.is_empty() + && block1.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout index bce4bc702733..32a3127b85a3 100644 --- a/tests/ui/author/issue_3849.stdout +++ b/tests/ui/author/issue_3849.stdout @@ -1,14 +1,12 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Call(func, args) = init.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, &["std", "mem", "transmute"]); - if args.len() == 1; - if let ExprKind::Path(ref qpath1) = args[0].kind; - if match_qpath(qpath1, &["ZPTR"]); - if let PatKind::Wild = local.pat.kind; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Call(func, args) = init.kind + && let ExprKind::Path(ref qpath) = func.kind + && match_qpath(qpath, &["std", "mem", "transmute"]) + && args.len() == 1 + && let ExprKind::Path(ref qpath1) = args[0].kind + && match_qpath(qpath1, &["ZPTR"]) + && let PatKind::Wild = local.pat.kind +{ + // report your lint here } diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout index ceb53fcd4963..94a6436ed547 100644 --- a/tests/ui/author/loop.stdout +++ b/tests/ui/author/loop.stdout @@ -1,113 +1,101 @@ -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind; - if name.as_str() == "y"; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local) = block.stmts[0].kind; - if let Some(init) = local.init; - if let ExprKind::Path(ref qpath1) = init.kind; - if match_qpath(qpath1, &["y"]); - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind; - if name1.as_str() == "z"; - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = pat.kind + && name.as_str() == "y" + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Path(ref qpath1) = init.kind + && match_qpath(qpath1, &["y"]) + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind + && name1.as_str() == "z" + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Wild = pat.kind; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Wild = pat.kind + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr); - if let PatKind::Wild = pat.kind; - if let ExprKind::Struct(qpath, fields, None) = arg.kind; - if matches!(qpath, QPath::LangItem(LangItem::Range, _)); - if fields.len() == 2; - if fields[0].ident.as_str() == "start"; - if let ExprKind::Lit(ref lit) = fields[0].expr.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node; - if fields[1].ident.as_str() == "end"; - if let ExprKind::Lit(ref lit1) = fields[1].expr.kind; - if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if let Some(label) = destination.label; - if label.ident.as_str() == "'label"; - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) + && let PatKind::Wild = pat.kind + && let ExprKind::Struct(qpath, fields, None) = arg.kind + && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && fields.len() == 2 + && fields[0].ident.as_str() == "start" + && let ExprKind::Lit(ref lit) = fields[0].expr.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + && fields[1].ident.as_str() == "end" + && let ExprKind::Lit(ref lit1) = fields[1].expr.kind + && let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && let Some(label) = destination.label + && label.ident.as_str() == "'label" + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr); - if let ExprKind::Path(ref qpath) = condition.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = body.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr) + && let ExprKind::Path(ref qpath) = condition.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = body.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr); - if let PatKind::Lit(lit_expr) = let_pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Path(ref qpath) = let_expr.kind; - if match_qpath(qpath, &["a"]); - if let ExprKind::Block(block, None) = if_then.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(e) = block.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if block.expr.is_none(); - then { - // report your lint here - } +if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr) + && let PatKind::Lit(lit_expr) = let_pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Path(ref qpath) = let_expr.kind + && match_qpath(qpath, &["a"]) + && let ExprKind::Block(block, None) = if_then.kind + && block.stmts.len() == 1 + && let StmtKind::Semi(e) = block.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind; - if body.stmts.len() == 1; - if let StmtKind::Semi(e) = body.stmts[0].kind; - if let ExprKind::Break(destination, None) = e.kind; - if destination.label.is_none(); - if body.expr.is_none(); - then { - // report your lint here - } +if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind + && body.stmts.len() == 1 + && let StmtKind::Semi(e) = body.stmts[0].kind + && let ExprKind::Break(destination, None) = e.kind + && destination.label.is_none() + && body.expr.is_none() +{ + // report your lint here } diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout index 2cf69a035b4c..88e2ca656a4f 100644 --- a/tests/ui/author/matches.stdout +++ b/tests/ui/author/matches.stdout @@ -1,38 +1,36 @@ -if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let Some(init) = local.init; - if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind; - if let ExprKind::Lit(ref lit) = scrutinee.kind; - if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node; - if arms.len() == 3; - if let PatKind::Lit(lit_expr) = arms[0].pat.kind; - if let ExprKind::Lit(ref lit1) = lit_expr.kind; - if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node; - if arms[0].guard.is_none(); - if let ExprKind::Lit(ref lit2) = arms[0].body.kind; - if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node; - if let PatKind::Lit(lit_expr1) = arms[1].pat.kind; - if let ExprKind::Lit(ref lit3) = lit_expr1.kind; - if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node; - if arms[1].guard.is_none(); - if let ExprKind::Block(block, None) = arms[1].body.kind; - if block.stmts.len() == 1; - if let StmtKind::Local(local1) = block.stmts[0].kind; - if let Some(init1) = local1.init; - if let ExprKind::Lit(ref lit4) = init1.kind; - if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind; - if name.as_str() == "x"; - if let Some(trailing_expr) = block.expr; - if let ExprKind::Path(ref qpath) = trailing_expr.kind; - if match_qpath(qpath, &["x"]); - if let PatKind::Wild = arms[2].pat.kind; - if arms[2].guard.is_none(); - if let ExprKind::Lit(ref lit5) = arms[2].body.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node; - if let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind; - if name1.as_str() == "a"; - then { - // report your lint here - } +if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind + && let ExprKind::Lit(ref lit) = scrutinee.kind + && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node + && arms.len() == 3 + && let PatKind::Lit(lit_expr) = arms[0].pat.kind + && let ExprKind::Lit(ref lit1) = lit_expr.kind + && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node + && arms[0].guard.is_none() + && let ExprKind::Lit(ref lit2) = arms[0].body.kind + && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node + && let PatKind::Lit(lit_expr1) = arms[1].pat.kind + && let ExprKind::Lit(ref lit3) = lit_expr1.kind + && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node + && arms[1].guard.is_none() + && let ExprKind::Block(block, None) = arms[1].body.kind + && block.stmts.len() == 1 + && let StmtKind::Local(local1) = block.stmts[0].kind + && let Some(init1) = local1.init + && let ExprKind::Lit(ref lit4) = init1.kind + && let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name, None) = local1.pat.kind + && name.as_str() == "x" + && let Some(trailing_expr) = block.expr + && let ExprKind::Path(ref qpath) = trailing_expr.kind + && match_qpath(qpath, &["x"]) + && let PatKind::Wild = arms[2].pat.kind + && arms[2].guard.is_none() + && let ExprKind::Lit(ref lit5) = arms[2].body.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node + && let PatKind::Binding(BindingAnnotation::NONE, _, name1, None) = local.pat.kind + && name1.as_str() == "a" +{ + // report your lint here } diff --git a/tests/ui/author/repeat.stdout b/tests/ui/author/repeat.stdout index 471bbce4f418..c2a369610cc1 100644 --- a/tests/ui/author/repeat.stdout +++ b/tests/ui/author/repeat.stdout @@ -1,12 +1,10 @@ -if_chain! { - if let ExprKind::Repeat(value, length) = expr.kind; - if let ExprKind::Lit(ref lit) = value.kind; - if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node; - if let ArrayLen::Body(anon_const) = length; - let expr1 = &cx.tcx.hir().body(anon_const.body).value; - if let ExprKind::Lit(ref lit1) = expr1.kind; - if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node; - then { - // report your lint here - } +if let ExprKind::Repeat(value, length) = expr.kind + && let ExprKind::Lit(ref lit) = value.kind + && let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node + && let ArrayLen::Body(anon_const) = length + && expr1 = &cx.tcx.hir().body(anon_const.body).value + && let ExprKind::Lit(ref lit1) = expr1.kind + && let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node +{ + // report your lint here } diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout index b5bbc9e213c6..0b332d5e7d0e 100644 --- a/tests/ui/author/struct.stdout +++ b/tests/ui/author/struct.stdout @@ -1,64 +1,56 @@ -if_chain! { - if let ExprKind::Struct(qpath, fields, None) = expr.kind; - if match_qpath(qpath, &["Test"]); - if fields.len() == 1; - if fields[0].ident.as_str() == "field"; - if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind; - if let ExprKind::DropTemps(expr1) = cond.kind; - if let ExprKind::Lit(ref lit) = expr1.kind; - if let LitKind::Bool(true) = lit.node; - if let ExprKind::Block(block, None) = then.kind; - if block.stmts.is_empty(); - if let Some(trailing_expr) = block.expr; - if let ExprKind::Lit(ref lit1) = trailing_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node; - if let ExprKind::Block(block1, None) = else_expr.kind; - if block1.stmts.is_empty(); - if let Some(trailing_expr1) = block1.expr; - if let ExprKind::Lit(ref lit2) = trailing_expr1.kind; - if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node; - then { - // report your lint here - } +if let ExprKind::Struct(qpath, fields, None) = expr.kind + && match_qpath(qpath, &["Test"]) + && fields.len() == 1 + && fields[0].ident.as_str() == "field" + && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind + && let ExprKind::DropTemps(expr1) = cond.kind + && let ExprKind::Lit(ref lit) = expr1.kind + && let LitKind::Bool(true) = lit.node + && let ExprKind::Block(block, None) = then.kind + && block.stmts.is_empty() + && let Some(trailing_expr) = block.expr + && let ExprKind::Lit(ref lit1) = trailing_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node + && let ExprKind::Block(block1, None) = else_expr.kind + && block1.stmts.is_empty() + && let Some(trailing_expr1) = block1.expr + && let ExprKind::Lit(ref lit2) = trailing_expr1.kind + && let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node +{ + // report your lint here } -if_chain! { - if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind; - if match_qpath(qpath, &["Test"]); - if fields.len() == 1; - if fields[0].ident.as_str() == "field"; - if let PatKind::Lit(lit_expr) = fields[0].pat.kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if arm.guard.is_none(); - if let ExprKind::Block(block, None) = arm.body.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind + && match_qpath(qpath, &["Test"]) + && fields.len() == 1 + && fields[0].ident.as_str() == "field" + && let PatKind::Lit(lit_expr) = fields[0].pat.kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && arm.guard.is_none() + && let ExprKind::Block(block, None) = arm.body.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind; - if match_qpath(qpath, &["TestTuple"]); - if fields.len() == 1; - if let PatKind::Lit(lit_expr) = fields[0].kind; - if let ExprKind::Lit(ref lit) = lit_expr.kind; - if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; - if arm.guard.is_none(); - if let ExprKind::Block(block, None) = arm.body.kind; - if block.stmts.is_empty(); - if block.expr.is_none(); - then { - // report your lint here - } +if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind + && match_qpath(qpath, &["TestTuple"]) + && fields.len() == 1 + && let PatKind::Lit(lit_expr) = fields[0].kind + && let ExprKind::Lit(ref lit) = lit_expr.kind + && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node + && arm.guard.is_none() + && let ExprKind::Block(block, None) = arm.body.kind + && block.stmts.is_empty() + && block.expr.is_none() +{ + // report your lint here } -if_chain! { - if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind; - if method_name.ident.as_str() == "test"; - if let ExprKind::Path(ref qpath) = receiver.kind; - if match_qpath(qpath, &["test_method_call"]); - if args.is_empty(); - then { - // report your lint here - } +if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind + && method_name.ident.as_str() == "test" + && let ExprKind::Path(ref qpath) = receiver.kind + && match_qpath(qpath, &["test_method_call"]) + && args.is_empty() +{ + // report your lint here } diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed new file mode 100644 index 000000000000..911fa856aa0a --- /dev/null +++ b/tests/ui/box_default.fixed @@ -0,0 +1,57 @@ +// run-rustfix +#![warn(clippy::box_default)] + +#[derive(Default)] +struct ImplementsDefault; + +struct OwnDefault; + +impl OwnDefault { + fn default() -> Self { + Self + } +} + +macro_rules! outer { + ($e: expr) => { + $e + }; +} + +fn main() { + let _string: Box = Box::default(); + let _byte = Box::::default(); + let _vec = Box::>::default(); + let _impl = Box::::default(); + let _impl2 = Box::::default(); + let _impl3: Box = Box::default(); + let _own = Box::new(OwnDefault::default()); // should not lint + let _in_macro = outer!(Box::::default()); + let _string_default = outer!(Box::::default()); + let _vec2: Box> = Box::default(); + let _vec3: Box> = Box::default(); + let _vec4: Box<_> = Box::>::default(); + let _more = ret_ty_fn(); + call_ty_fn(Box::default()); +} + +fn ret_ty_fn() -> Box { + Box::::default() +} + +#[allow(clippy::boxed_local)] +fn call_ty_fn(_b: Box) { + issue_9621_dyn_trait(); +} + +use std::io::{Read, Result}; + +impl Read for ImplementsDefault { + fn read(&mut self, _: &mut [u8]) -> Result { + Ok(0) + } +} + +fn issue_9621_dyn_trait() { + let _: Box = Box::::default(); +} diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index dc522705bc62..20019c2ee5a0 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::box_default)] #[derive(Default)] @@ -26,6 +27,31 @@ fn main() { let _impl3: Box = Box::new(Default::default()); let _own = Box::new(OwnDefault::default()); // should not lint let _in_macro = outer!(Box::new(String::new())); - // false negative: default is from different expansion + let _string_default = outer!(Box::new(String::from(""))); let _vec2: Box> = Box::new(vec![]); + let _vec3: Box> = Box::new(Vec::from([])); + let _vec4: Box<_> = Box::new(Vec::from([false; 0])); + let _more = ret_ty_fn(); + call_ty_fn(Box::new(u8::default())); +} + +fn ret_ty_fn() -> Box { + Box::new(bool::default()) +} + +#[allow(clippy::boxed_local)] +fn call_ty_fn(_b: Box) { + issue_9621_dyn_trait(); +} + +use std::io::{Read, Result}; + +impl Read for ImplementsDefault { + fn read(&mut self, _: &mut [u8]) -> Result { + Ok(0) + } +} + +fn issue_9621_dyn_trait() { + let _: Box = Box::new(ImplementsDefault::default()); } diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index b2030e95acb1..5ea410331afb 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -1,59 +1,88 @@ error: `Box::new(_)` of default value - --> $DIR/box_default.rs:21:32 + --> $DIR/box_default.rs:22:32 | LL | let _string: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` | - = help: use `Box::default()` instead = note: `-D clippy::box-default` implied by `-D warnings` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:22:17 + --> $DIR/box_default.rs:23:17 | LL | let _byte = Box::new(u8::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:23:16 + --> $DIR/box_default.rs:24:16 | LL | let _vec = Box::new(Vec::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:24:17 + --> $DIR/box_default.rs:25:17 | LL | let _impl = Box::new(ImplementsDefault::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:25:18 + --> $DIR/box_default.rs:26:18 | LL | let _impl2 = Box::new(::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:26:42 + --> $DIR/box_default.rs:27:42 | LL | let _impl3: Box = Box::new(Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use `Box::default()` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:28:28 + --> $DIR/box_default.rs:29:28 | LL | let _in_macro = outer!(Box::new(String::new())); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:30:34 + | +LL | let _string_default = outer!(Box::new(String::from(""))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:31:46 + | +LL | let _vec2: Box> = Box::new(vec![]); + | ^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:32:33 + | +LL | let _vec3: Box> = Box::new(Vec::from([])); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:33:25 + | +LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0])); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:35:16 + | +LL | call_ty_fn(Box::new(u8::default())); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:39:5 + | +LL | Box::new(bool::default()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:56:28 | - = help: use `Box::default()` instead +LL | let _: Box = Box::new(ImplementsDefault::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` -error: aborting due to 7 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/box_default_no_std.rs b/tests/ui/box_default_no_std.rs new file mode 100644 index 000000000000..4326abc9a541 --- /dev/null +++ b/tests/ui/box_default_no_std.rs @@ -0,0 +1,33 @@ +#![feature(lang_items, start, libc)] +#![warn(clippy::box_default)] +#![no_std] + +pub struct NotBox { + _value: T, +} + +impl NotBox { + pub fn new(value: T) -> Self { + Self { _value: value } + } +} + +impl Default for NotBox { + fn default() -> Self { + Self::new(T::default()) + } +} + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _p = NotBox::new(isize::default()); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index a37f3fec20f1..e6bf944c7a5e 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,6 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +32,17 @@ fn main() { let _ = (x as i64 - y as i64).unsigned_abs() as u32; } + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + assert_eq!(10u32, x.unsigned_abs()); +} diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 5706930af5a0..c87320b5209d 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,6 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +32,17 @@ fn main() { let _ = (x as i64 - y as i64).abs() as u32; } + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 7cea11c183d2..1b39c554b038 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:7:18 + --> $DIR/cast_abs_to_unsigned.rs:9:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,100 +7,106 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:11:20 + --> $DIR/cast_abs_to_unsigned.rs:13:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:12:20 + --> $DIR/cast_abs_to_unsigned.rs:14:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:13:13 + --> $DIR/cast_abs_to_unsigned.rs:15:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:16:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:17:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:22:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:21:13 + --> $DIR/cast_abs_to_unsigned.rs:23:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:24:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:25:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:30:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:29:13 + --> $DIR/cast_abs_to_unsigned.rs:31:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:31:13 + --> $DIR/cast_abs_to_unsigned.rs:33:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` -error: aborting due to 17 previous errors +error: casting the result of `i32::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:47:23 + | +LL | assert_eq!(10u32, x.abs() as u32); + | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/cast_lossless_bool.fixed b/tests/ui/cast_lossless_bool.fixed index 9e2da45c3785..af13b755e310 100644 --- a/tests/ui/cast_lossless_bool.fixed +++ b/tests/ui/cast_lossless_bool.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -40,3 +41,15 @@ mod cast_lossless_in_impl { } } } + +fn msrv_1_27() { + #![clippy::msrv = "1.27"] + + let _ = true as u8; +} + +fn msrv_1_28() { + #![clippy::msrv = "1.28"] + + let _ = u8::from(true); +} diff --git a/tests/ui/cast_lossless_bool.rs b/tests/ui/cast_lossless_bool.rs index b6f6c59a01f9..3b06af899c60 100644 --- a/tests/ui/cast_lossless_bool.rs +++ b/tests/ui/cast_lossless_bool.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -40,3 +41,15 @@ mod cast_lossless_in_impl { } } } + +fn msrv_1_27() { + #![clippy::msrv = "1.27"] + + let _ = true as u8; +} + +fn msrv_1_28() { + #![clippy::msrv = "1.28"] + + let _ = true as u8; +} diff --git a/tests/ui/cast_lossless_bool.stderr b/tests/ui/cast_lossless_bool.stderr index 6b148336011d..768b033d10a2 100644 --- a/tests/ui/cast_lossless_bool.stderr +++ b/tests/ui/cast_lossless_bool.stderr @@ -1,5 +1,5 @@ error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> $DIR/cast_lossless_bool.rs:8:13 + --> $DIR/cast_lossless_bool.rs:9:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` @@ -7,76 +7,82 @@ LL | let _ = true as u8; = note: `-D clippy::cast-lossless` implied by `-D warnings` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:9:13 + --> $DIR/cast_lossless_bool.rs:10:13 | LL | let _ = true as u16; | ^^^^^^^^^^^ help: try: `u16::from(true)` error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)` - --> $DIR/cast_lossless_bool.rs:10:13 + --> $DIR/cast_lossless_bool.rs:11:13 | LL | let _ = true as u32; | ^^^^^^^^^^^ help: try: `u32::from(true)` error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)` - --> $DIR/cast_lossless_bool.rs:11:13 + --> $DIR/cast_lossless_bool.rs:12:13 | LL | let _ = true as u64; | ^^^^^^^^^^^ help: try: `u64::from(true)` error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)` - --> $DIR/cast_lossless_bool.rs:12:13 + --> $DIR/cast_lossless_bool.rs:13:13 | LL | let _ = true as u128; | ^^^^^^^^^^^^ help: try: `u128::from(true)` error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)` - --> $DIR/cast_lossless_bool.rs:13:13 + --> $DIR/cast_lossless_bool.rs:14:13 | LL | let _ = true as usize; | ^^^^^^^^^^^^^ help: try: `usize::from(true)` error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)` - --> $DIR/cast_lossless_bool.rs:15:13 + --> $DIR/cast_lossless_bool.rs:16:13 | LL | let _ = true as i8; | ^^^^^^^^^^ help: try: `i8::from(true)` error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)` - --> $DIR/cast_lossless_bool.rs:16:13 + --> $DIR/cast_lossless_bool.rs:17:13 | LL | let _ = true as i16; | ^^^^^^^^^^^ help: try: `i16::from(true)` error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)` - --> $DIR/cast_lossless_bool.rs:17:13 + --> $DIR/cast_lossless_bool.rs:18:13 | LL | let _ = true as i32; | ^^^^^^^^^^^ help: try: `i32::from(true)` error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)` - --> $DIR/cast_lossless_bool.rs:18:13 + --> $DIR/cast_lossless_bool.rs:19:13 | LL | let _ = true as i64; | ^^^^^^^^^^^ help: try: `i64::from(true)` error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)` - --> $DIR/cast_lossless_bool.rs:19:13 + --> $DIR/cast_lossless_bool.rs:20:13 | LL | let _ = true as i128; | ^^^^^^^^^^^^ help: try: `i128::from(true)` error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)` - --> $DIR/cast_lossless_bool.rs:20:13 + --> $DIR/cast_lossless_bool.rs:21:13 | LL | let _ = true as isize; | ^^^^^^^^^^^^^ help: try: `isize::from(true)` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:23:13 + --> $DIR/cast_lossless_bool.rs:24:13 | LL | let _ = (true | false) as u16; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` -error: aborting due to 13 previous errors +error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` + --> $DIR/cast_lossless_bool.rs:54:13 + | +LL | let _ = true as u8; + | ^^^^^^^^^^ help: try: `u8::from(true)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/cast_nan_to_int.rs b/tests/ui/cast_nan_to_int.rs new file mode 100644 index 000000000000..287c5aa216bd --- /dev/null +++ b/tests/ui/cast_nan_to_int.rs @@ -0,0 +1,18 @@ +#![warn(clippy::cast_nan_to_int)] +#![allow(clippy::eq_op)] + +fn main() { + let _ = (0.0_f32 / -0.0) as usize; + let _ = (f64::INFINITY * -0.0) as usize; + let _ = (0.0 * f32::INFINITY) as usize; + + let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize; + let _ = (f32::INFINITY - f32::INFINITY) as usize; + let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize; + + // those won't be linted: + let _ = (1.0_f32 / 0.0) as usize; + let _ = (f32::INFINITY * f32::NEG_INFINITY) as usize; + let _ = (f32::INFINITY - f32::NEG_INFINITY) as usize; + let _ = (f64::INFINITY - 0.0) as usize; +} diff --git a/tests/ui/cast_nan_to_int.stderr b/tests/ui/cast_nan_to_int.stderr new file mode 100644 index 000000000000..3539be75a19d --- /dev/null +++ b/tests/ui/cast_nan_to_int.stderr @@ -0,0 +1,51 @@ +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:5:13 + | +LL | let _ = (0.0_f32 / -0.0) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + = note: `-D clippy::cast-nan-to-int` implied by `-D warnings` + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:6:13 + | +LL | let _ = (f64::INFINITY * -0.0) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:7:13 + | +LL | let _ = (0.0 * f32::INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:9:13 + | +LL | let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:10:13 + | +LL | let _ = (f32::INFINITY - f32::INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: casting a known NaN to usize + --> $DIR/cast_nan_to_int.rs:11:13 + | +LL | let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this always evaluates to 0 + +error: aborting due to 6 previous errors + diff --git a/tests/ui/cfg_attr_rustfmt.fixed b/tests/ui/cfg_attr_rustfmt.fixed index 061a4ab9b2ef..8a5645b22ed1 100644 --- a/tests/ui/cfg_attr_rustfmt.fixed +++ b/tests/ui/cfg_attr_rustfmt.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, custom_inner_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -29,3 +29,17 @@ mod foo { pub fn f() {} } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + #[rustfmt::skip] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.rs b/tests/ui/cfg_attr_rustfmt.rs index 035169fab85b..2fb140efae76 100644 --- a/tests/ui/cfg_attr_rustfmt.rs +++ b/tests/ui/cfg_attr_rustfmt.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, custom_inner_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -29,3 +29,17 @@ mod foo { pub fn f() {} } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.stderr b/tests/ui/cfg_attr_rustfmt.stderr index c1efd47db90b..08df7b2b39a0 100644 --- a/tests/ui/cfg_attr_rustfmt.stderr +++ b/tests/ui/cfg_attr_rustfmt.stderr @@ -12,5 +12,11 @@ error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes LL | #[cfg_attr(rustfmt, rustfmt_skip)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` -error: aborting due to 2 previous errors +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes + --> $DIR/cfg_attr_rustfmt.rs:43:5 + | +LL | #[cfg_attr(rustfmt, rustfmt::skip)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` + +error: aborting due to 3 previous errors diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index cb7100bc9efa..f936957cb40c 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -1,7 +1,9 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +78,18 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +fn msrv_1_33() { + #![clippy::msrv = "1.33"] + + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let value: i64 = 34; + let _ = u32::try_from(value).is_ok(); +} + fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index ed4e0692388a..77aec713ff31 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -1,7 +1,9 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +78,18 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +fn msrv_1_33() { + #![clippy::msrv = "1.33"] + + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let value: i64 = 34; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + fn main() {} diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 2e518040561c..b2bf7af8daf8 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:15:13 + --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,94 +7,100 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = note: `-D clippy::checked-conversions` implied by `-D warnings` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:16:13 + --> $DIR/checked_conversions.rs:18:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:20:13 + --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:21:13 + --> $DIR/checked_conversions.rs:23:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:25:13 + --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:26:13 + --> $DIR/checked_conversions.rs:28:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:32:13 + --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:33:13 + --> $DIR/checked_conversions.rs:35:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:37:13 + --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:38:13 + --> $DIR/checked_conversions.rs:40:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:44:13 + --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:45:13 + --> $DIR/checked_conversions.rs:47:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:49:13 + --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:50:13 + --> $DIR/checked_conversions.rs:52:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:54:13 + --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:55:13 + --> $DIR/checked_conversions.rs:57:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: aborting due to 16 previous errors +error: checked cast can be simplified + --> $DIR/checked_conversions.rs:92:13 + | +LL | let _ = value <= (u32::MAX as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` + +error: aborting due to 17 previous errors diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed index 4eb999e18e64..42ed232d1001 100644 --- a/tests/ui/cloned_instead_of_copied.fixed +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -1,5 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +16,24 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).copied(); // Option::copied needs 1.35 +} + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + let _ = [1].iter().copied(); // Iterator::copied needs 1.36 + let _ = Some(&1).copied(); +} diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs index 894496c0ebbb..471bd9654cc1 100644 --- a/tests/ui/cloned_instead_of_copied.rs +++ b/tests/ui/cloned_instead_of_copied.rs @@ -1,5 +1,8 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +16,24 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); // Option::copied needs 1.35 +} + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + let _ = Some(&1).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr index e0707d321468..914c9a91e830 100644 --- a/tests/ui/cloned_instead_of_copied.stderr +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -1,5 +1,5 @@ error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:6:24 + --> $DIR/cloned_instead_of_copied.rs:9:24 | LL | let _ = [1].iter().cloned(); | ^^^^^^ help: try: `copied` @@ -7,28 +7,46 @@ LL | let _ = [1].iter().cloned(); = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:7:31 + --> $DIR/cloned_instead_of_copied.rs:10:31 | LL | let _ = vec!["hi"].iter().cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:8:22 + --> $DIR/cloned_instead_of_copied.rs:11:22 | LL | let _ = Some(&1).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:9:34 + --> $DIR/cloned_instead_of_copied.rs:12:34 | LL | let _ = Box::new([1].iter()).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:10:32 + --> $DIR/cloned_instead_of_copied.rs:13:32 | LL | let _ = Box::new(Some(&1)).cloned(); | ^^^^^^ help: try: `copied` -error: aborting due to 5 previous errors +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:31:22 + | +LL | let _ = Some(&1).cloned(); // Option::copied needs 1.35 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:37:24 + | +LL | let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:38:22 + | +LL | let _ = Some(&1).cloned(); + | ^^^^^^ help: try: `copied` + +error: aborting due to 8 previous errors diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 7d53e08345d3..1d7a72846419 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -253,6 +253,27 @@ fn negative_cases(res_opt: Result, String>, res_res: Result>, b: () }, + B, +} + +pub fn test_1(x: Issue9647) { + if let Issue9647::A { a, .. } = x { + if let Some(u) = a { + println!("{u:?}") + } + } +} + +pub fn test_2(x: Issue9647) { + if let Issue9647::A { a: Some(a), .. } = x { + if let Some(u) = a { + println!("{u}") + } + } +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 2580bef58091..0294be60b43f 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -175,5 +175,37 @@ LL | Some(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: aborting due to 10 previous errors +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:263:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u:?}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:262:27 + | +LL | if let Issue9647::A { a, .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern, prefixed by a: + +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:271:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:270:35 + | +LL | if let Issue9647::A { a: Some(a), .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern + +error: aborting due to 12 previous errors diff --git a/tests/ui/crashes/ice-9625.rs b/tests/ui/crashes/ice-9625.rs new file mode 100644 index 000000000000..a765882b5d81 --- /dev/null +++ b/tests/ui/crashes/ice-9625.rs @@ -0,0 +1,4 @@ +fn main() { + let x = &1; + let _ = &1 < x && x < &10; +} diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index a28bff76755b..a370ccc76962 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -33,6 +33,7 @@ mod basic_expr { let x: [f64; 3] = [1., 2., 3.]; let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) }; let x: _ = 1.; + const X: f32 = 1.; } } @@ -59,6 +60,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2. }; + + const X: f32 = { + // Should lint this because this literal is not bound to any types. + let y = 1.0_f64; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1. + }; } } diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index b48435cc7b28..2476fe95141d 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -33,6 +33,7 @@ mod basic_expr { let x: [f64; 3] = [1., 2., 3.]; let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) }; let x: _ = 1.; + const X: f32 = 1.; } } @@ -59,6 +60,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2. }; + + const X: f32 = { + // Should lint this because this literal is not bound to any types. + let y = 1.; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1. + }; } } diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index f8b6c7746edb..5df2f642388d 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -61,79 +61,85 @@ LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:43:21 + --> $DIR/default_numeric_fallback_f64.rs:44:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:51:21 + --> $DIR/default_numeric_fallback_f64.rs:52:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:57:21 + --> $DIR/default_numeric_fallback_f64.rs:58:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:69:9 + --> $DIR/default_numeric_fallback_f64.rs:66:21 + | +LL | let y = 1.; + | ^^ help: consider adding suffix: `1.0_f64` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_f64.rs:78:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:75:27 + --> $DIR/default_numeric_fallback_f64.rs:84:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:79:29 + --> $DIR/default_numeric_fallback_f64.rs:88:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:93:21 + --> $DIR/default_numeric_fallback_f64.rs:102:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:96:32 + --> $DIR/default_numeric_fallback_f64.rs:105:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:114:28 + --> $DIR/default_numeric_fallback_f64.rs:123:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:117:36 + --> $DIR/default_numeric_fallback_f64.rs:126:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:135:24 + --> $DIR/default_numeric_fallback_f64.rs:144:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:155:23 + --> $DIR/default_numeric_fallback_f64.rs:164:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:162:21 + --> $DIR/default_numeric_fallback_f64.rs:171:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` @@ -143,5 +149,5 @@ LL | internal_macro!(); | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 23 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index 55451cf2f7d0..3f4994f0453b 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -33,6 +33,8 @@ mod basic_expr { let x: [i32; 3] = [1, 2, 3]; let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; let x: _ = 1; + let x: u64 = 1; + const CONST_X: i8 = 1; } } @@ -59,6 +61,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2 }; + + const CONST_X: i32 = { + // Should lint this because this literal is not bound to any types. + let y = 1_i32; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; } } diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 62d72f2febaa..2df0e09787f9 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -33,6 +33,8 @@ mod basic_expr { let x: [i32; 3] = [1, 2, 3]; let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; let x: _ = 1; + let x: u64 = 1; + const CONST_X: i8 = 1; } } @@ -59,6 +61,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2 }; + + const CONST_X: i32 = { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; } } diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index f7c5e724c403..6f219c3fc2b0 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -73,79 +73,85 @@ LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:43:21 + --> $DIR/default_numeric_fallback_i32.rs:45:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:51:21 + --> $DIR/default_numeric_fallback_i32.rs:53:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:57:21 + --> $DIR/default_numeric_fallback_i32.rs:59:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:69:9 + --> $DIR/default_numeric_fallback_i32.rs:67:21 + | +LL | let y = 1; + | ^ help: consider adding suffix: `1_i32` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_i32.rs:79:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:75:27 + --> $DIR/default_numeric_fallback_i32.rs:85:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:79:29 + --> $DIR/default_numeric_fallback_i32.rs:89:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:93:21 + --> $DIR/default_numeric_fallback_i32.rs:103:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:96:32 + --> $DIR/default_numeric_fallback_i32.rs:106:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:114:28 + --> $DIR/default_numeric_fallback_i32.rs:124:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:117:36 + --> $DIR/default_numeric_fallback_i32.rs:127:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:135:24 + --> $DIR/default_numeric_fallback_i32.rs:145:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:155:23 + --> $DIR/default_numeric_fallback_i32.rs:165:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:162:21 + --> $DIR/default_numeric_fallback_i32.rs:172:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -155,5 +161,5 @@ LL | internal_macro!(); | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 25 previous errors +error: aborting due to 26 previous errors diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed index 7e18d70bae40..3bac738acd65 100644 --- a/tests/ui/err_expect.fixed +++ b/tests/ui/err_expect.fixed @@ -1,5 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +15,17 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let x: Result = Ok(16); + x.err().expect("16"); +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let x: Result = Ok(17); + x.expect_err("17"); +} diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs index bf8c3c9fb8c9..6e7c47d9ad3c 100644 --- a/tests/ui/err_expect.rs +++ b/tests/ui/err_expect.rs @@ -1,5 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +15,17 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let x: Result = Ok(16); + x.err().expect("16"); +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let x: Result = Ok(17); + x.err().expect("17"); +} diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr index ffd97e00a5c0..91a6cf8de65f 100644 --- a/tests/ui/err_expect.stderr +++ b/tests/ui/err_expect.stderr @@ -1,10 +1,16 @@ error: called `.err().expect()` on a `Result` value - --> $DIR/err_expect.rs:10:16 + --> $DIR/err_expect.rs:13:16 | LL | test_debug.err().expect("Testing debug type"); | ^^^^^^^^^^^^ help: try: `expect_err` | = note: `-D clippy::err-expect` implied by `-D warnings` -error: aborting due to previous error +error: called `.err().expect()` on a `Result` value + --> $DIR/err_expect.rs:30:7 + | +LL | x.err().expect("17"); + | ^^^^^^^^^^^^ help: try: `expect_err` + +error: aborting due to 2 previous errors diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed index c3992d7e92cf..41828ddd7acd 100644 --- a/tests/ui/filter_map_next_fixable.fixed +++ b/tests/ui/filter_map_next_fixable.fixed @@ -1,6 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +10,17 @@ fn main() { let element: Option = a.iter().find_map(|s| s.parse().ok()); assert_eq!(element, Some(1)); } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().find_map(|s| s.parse().ok()); +} diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs index 447219a96839..be492a81b45e 100644 --- a/tests/ui/filter_map_next_fixable.rs +++ b/tests/ui/filter_map_next_fixable.rs @@ -1,6 +1,8 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +10,17 @@ fn main() { let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); assert_eq!(element, Some(1)); } + +fn msrv_1_29() { + #![clippy::msrv = "1.29"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +fn msrv_1_30() { + #![clippy::msrv = "1.30"] + + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr index 3bb062ffd7a3..e789efeabd55 100644 --- a/tests/ui/filter_map_next_fixable.stderr +++ b/tests/ui/filter_map_next_fixable.stderr @@ -1,10 +1,16 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> $DIR/filter_map_next_fixable.rs:8:32 + --> $DIR/filter_map_next_fixable.rs:10:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` | = note: `-D clippy::filter-map-next` implied by `-D warnings` -error: aborting due to previous error +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead + --> $DIR/filter_map_next_fixable.rs:25:26 + | +LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` + +error: aborting due to 2 previous errors diff --git a/tests/ui/format_args.fixed b/tests/ui/format_args.fixed index 24cf0847dd58..825e122be5a5 100644 --- a/tests/ui/format_args.fixed +++ b/tests/ui/format_args.fixed @@ -3,6 +3,7 @@ #![allow(unused)] #![allow( clippy::assertions_on_constants, + clippy::double_parens, clippy::eq_op, clippy::print_literal, clippy::uninlined_format_args @@ -114,6 +115,8 @@ fn main() { println!("error: something failed at {}", my_other_macro!()); // https://github.com/rust-lang/rust-clippy/issues/7903 println!("{foo}{foo:?}", foo = "foo".to_string()); + print!("{}", (Location::caller())); + print!("{}", ((Location::caller()))); } fn issue8643(vendor_id: usize, product_id: usize, name: &str) { diff --git a/tests/ui/format_args.rs b/tests/ui/format_args.rs index 753babf0afdc..a41e53389e52 100644 --- a/tests/ui/format_args.rs +++ b/tests/ui/format_args.rs @@ -3,6 +3,7 @@ #![allow(unused)] #![allow( clippy::assertions_on_constants, + clippy::double_parens, clippy::eq_op, clippy::print_literal, clippy::uninlined_format_args @@ -114,6 +115,8 @@ fn main() { println!("error: something failed at {}", my_other_macro!()); // https://github.com/rust-lang/rust-clippy/issues/7903 println!("{foo}{foo:?}", foo = "foo".to_string()); + print!("{}", (Location::caller().to_string())); + print!("{}", ((Location::caller()).to_string())); } fn issue8643(vendor_id: usize, product_id: usize, name: &str) { diff --git a/tests/ui/format_args.stderr b/tests/ui/format_args.stderr index 68b0bb9e089e..f1832b970198 100644 --- a/tests/ui/format_args.stderr +++ b/tests/ui/format_args.stderr @@ -1,5 +1,5 @@ error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:76:72 + --> $DIR/format_args.rs:77:72 | LL | let _ = format!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this @@ -7,136 +7,148 @@ LL | let _ = format!("error: something failed at {}", Location::caller().to_ = note: `-D clippy::to-string-in-format-args` implied by `-D warnings` error: `to_string` applied to a type that implements `Display` in `write!` args - --> $DIR/format_args.rs:80:27 + --> $DIR/format_args.rs:81:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `writeln!` args - --> $DIR/format_args.rs:85:27 + --> $DIR/format_args.rs:86:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> $DIR/format_args.rs:87:63 + --> $DIR/format_args.rs:88:63 | LL | print!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:88:65 + --> $DIR/format_args.rs:89:65 | LL | println!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprint!` args - --> $DIR/format_args.rs:89:64 + --> $DIR/format_args.rs:90:64 | LL | eprint!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprintln!` args - --> $DIR/format_args.rs:90:66 + --> $DIR/format_args.rs:91:66 | LL | eprintln!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format_args!` args - --> $DIR/format_args.rs:91:77 + --> $DIR/format_args.rs:92:77 | LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert!` args - --> $DIR/format_args.rs:92:70 + --> $DIR/format_args.rs:93:70 | LL | assert!(true, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_eq!` args - --> $DIR/format_args.rs:93:73 + --> $DIR/format_args.rs:94:73 | LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_ne!` args - --> $DIR/format_args.rs:94:73 + --> $DIR/format_args.rs:95:73 | LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `panic!` args - --> $DIR/format_args.rs:95:63 + --> $DIR/format_args.rs:96:63 | LL | panic!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:96:20 + --> $DIR/format_args.rs:97:20 | LL | println!("{}", X(1).to_string()); | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:97:20 + --> $DIR/format_args.rs:98:20 | LL | println!("{}", Y(&X(1)).to_string()); | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:98:24 + --> $DIR/format_args.rs:99:24 | LL | println!("{}", Z(1).to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:99:20 + --> $DIR/format_args.rs:100:20 | LL | println!("{}", x.to_string()); | ^^^^^^^^^^^^^ help: use this: `**x` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:100:20 + --> $DIR/format_args.rs:101:20 | LL | println!("{}", x_ref.to_string()); | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:102:39 + --> $DIR/format_args.rs:103:39 | LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:103:52 + --> $DIR/format_args.rs:104:52 | LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:104:39 + --> $DIR/format_args.rs:105:39 | LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:105:52 + --> $DIR/format_args.rs:106:52 | LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); | ^^^^^^^^^^^^ help: remove this +error: `to_string` applied to a type that implements `Display` in `print!` args + --> $DIR/format_args.rs:118:37 + | +LL | print!("{}", (Location::caller().to_string())); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `print!` args + --> $DIR/format_args.rs:119:39 + | +LL | print!("{}", ((Location::caller()).to_string())); + | ^^^^^^^^^^^^ help: remove this + error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:144:38 + --> $DIR/format_args.rs:147:38 | LL | let x = format!("{} {}", a, b.to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:158:24 + --> $DIR/format_args.rs:161:24 | LL | println!("{}", original[..10].to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed new file mode 100644 index 000000000000..1cf49ca45f49 --- /dev/null +++ b/tests/ui/from_over_into.fixed @@ -0,0 +1,87 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![warn(clippy::from_over_into)] +#![allow(unused)] + +// this should throw an error +struct StringWrapper(String); + +impl From for StringWrapper { + fn from(val: String) -> Self { + StringWrapper(val) + } +} + +struct SelfType(String); + +impl From for SelfType { + fn from(val: String) -> Self { + SelfType(String::new()) + } +} + +#[derive(Default)] +struct X; + +impl X { + const FOO: &'static str = "a"; +} + +struct SelfKeywords; + +impl From for SelfKeywords { + fn from(val: X) -> Self { + let _ = X::default(); + let _ = X::FOO; + let _: X = val; + + SelfKeywords + } +} + +struct ExplicitPaths(bool); + +impl core::convert::From for bool { + fn from(mut val: crate::ExplicitPaths) -> Self { + let in_closure = || val.0; + + val.0 = false; + val.0 + } +} + +// this is fine +struct A(String); + +impl From for A { + fn from(s: String) -> A { + A(s) + } +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + struct FromOverInto(Vec); + + impl From> for FromOverInto { + fn from(val: Vec) -> Self { + FromOverInto(val) + } + } +} + +fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 292d0924fb17..d30f3c3fc925 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,4 +1,8 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] +#![allow(unused)] // this should throw an error struct StringWrapper(String); @@ -9,6 +13,44 @@ impl Into for String { } } +struct SelfType(String); + +impl Into for String { + fn into(self) -> SelfType { + SelfType(Self::new()) + } +} + +#[derive(Default)] +struct X; + +impl X { + const FOO: &'static str = "a"; +} + +struct SelfKeywords; + +impl Into for X { + fn into(self) -> SelfKeywords { + let _ = Self::default(); + let _ = Self::FOO; + let _: Self = self; + + SelfKeywords + } +} + +struct ExplicitPaths(bool); + +impl core::convert::Into for crate::ExplicitPaths { + fn into(mut self) -> bool { + let in_closure = || self.0; + + self.0 = false; + self.0 + } +} + // this is fine struct A(String); @@ -18,4 +60,28 @@ impl From for A { } } +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 469adadd2196..9c2a7c04c364 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,11 +1,75 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:6:1 + --> $DIR/from_over_into.rs:10:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider to implement `From` instead = note: `-D clippy::from-over-into` implied by `-D warnings` +help: replace the `Into` implentation with `From` + | +LL ~ impl From for StringWrapper { +LL ~ fn from(val: String) -> Self { +LL ~ StringWrapper(val) + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:18:1 + | +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From` + | +LL ~ impl From for SelfType { +LL ~ fn from(val: String) -> Self { +LL ~ SelfType(String::new()) + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:33:1 + | +LL | impl Into for X { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From` + | +LL ~ impl From for SelfKeywords { +LL ~ fn from(val: X) -> Self { +LL ~ let _ = X::default(); +LL ~ let _ = X::FOO; +LL ~ let _: X = val; + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:45:1 + | +LL | impl core::convert::Into for crate::ExplicitPaths { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence +help: replace the `Into` implentation with `From` + | +LL ~ impl core::convert::From for bool { +LL ~ fn from(mut val: crate::ExplicitPaths) -> Self { +LL ~ let in_closure = || val.0; +LL | +LL ~ val.0 = false; +LL ~ val.0 + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:80:5 + | +LL | impl Into> for Vec { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From>` + | +LL ~ impl From> for FromOverInto { +LL ~ fn from(val: Vec) -> Self { +LL ~ FromOverInto(val) + | -error: aborting due to previous error +error: aborting due to 5 previous errors diff --git a/tests/ui/from_over_into_unfixable.rs b/tests/ui/from_over_into_unfixable.rs new file mode 100644 index 000000000000..3b280b7488ae --- /dev/null +++ b/tests/ui/from_over_into_unfixable.rs @@ -0,0 +1,35 @@ +#![warn(clippy::from_over_into)] + +struct InMacro(String); + +macro_rules! in_macro { + ($e:ident) => { + $e + }; +} + +impl Into for String { + fn into(self) -> InMacro { + InMacro(in_macro!(self)) + } +} + +struct WeirdUpperSelf; + +impl Into for &'static [u8] { + fn into(self) -> WeirdUpperSelf { + let _ = Self::default(); + WeirdUpperSelf + } +} + +struct ContainsVal; + +impl Into for ContainsVal { + fn into(self) -> u8 { + let val = 1; + val + 1 + } +} + +fn main() {} diff --git a/tests/ui/from_over_into_unfixable.stderr b/tests/ui/from_over_into_unfixable.stderr new file mode 100644 index 000000000000..6f6ce351921b --- /dev/null +++ b/tests/ui/from_over_into_unfixable.stderr @@ -0,0 +1,29 @@ +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:11:1 + | +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace the `Into` implentation with `From` + = note: `-D clippy::from-over-into` implied by `-D warnings` + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:19:1 + | +LL | impl Into for &'static [u8] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace the `Into` implentation with `From<&'static [u8]>` + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:28:1 + | +LL | impl Into for ContainsVal { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence + = help: replace the `Into` implentation with `From` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index e6f57e9267ea..93df81b1a7ff 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -2,6 +2,21 @@ #![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)] #![warn(clippy::implicit_saturating_sub)] +use std::cmp::PartialEq; +use std::ops::SubAssign; +// Mock type +struct Mock; + +impl PartialEq for Mock { + fn eq(&self, _: &u32) -> bool { + true + } +} + +impl SubAssign for Mock { + fn sub_assign(&mut self, _: u32) {} +} + fn main() { // Tests for unsigned integers @@ -165,4 +180,39 @@ fn main() { } else { println!("side effect"); } + + // Extended tests + let mut m = Mock; + let mut u_32 = 3000; + let a = 200; + let mut _b = 8; + + if m != 0 { + m -= 1; + } + + if a > 0 { + _b -= 1; + } + + if 0 > a { + _b -= 1; + } + + if u_32 > 0 { + u_32 -= 1; + } else { + println!("don't lint this"); + } + + if u_32 > 0 { + println!("don't lint this"); + u_32 -= 1; + } + + if u_32 > 42 { + println!("brace yourself!"); + } else if u_32 > 0 { + u_32 -= 1; + } } diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 8bb28d149c62..8340bc8264d5 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -2,6 +2,21 @@ #![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)] #![warn(clippy::implicit_saturating_sub)] +use std::cmp::PartialEq; +use std::ops::SubAssign; +// Mock type +struct Mock; + +impl PartialEq for Mock { + fn eq(&self, _: &u32) -> bool { + true + } +} + +impl SubAssign for Mock { + fn sub_assign(&mut self, _: u32) {} +} + fn main() { // Tests for unsigned integers @@ -211,4 +226,39 @@ fn main() { } else { println!("side effect"); } + + // Extended tests + let mut m = Mock; + let mut u_32 = 3000; + let a = 200; + let mut _b = 8; + + if m != 0 { + m -= 1; + } + + if a > 0 { + _b -= 1; + } + + if 0 > a { + _b -= 1; + } + + if u_32 > 0 { + u_32 -= 1; + } else { + println!("don't lint this"); + } + + if u_32 > 0 { + println!("don't lint this"); + u_32 -= 1; + } + + if u_32 > 42 { + println!("brace yourself!"); + } else if u_32 > 0 { + u_32 -= 1; + } } diff --git a/tests/ui/implicit_saturating_sub.stderr b/tests/ui/implicit_saturating_sub.stderr index 5bb9a606422a..5e589d931e43 100644 --- a/tests/ui/implicit_saturating_sub.stderr +++ b/tests/ui/implicit_saturating_sub.stderr @@ -1,5 +1,5 @@ error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:13:5 + --> $DIR/implicit_saturating_sub.rs:28:5 | LL | / if u_8 > 0 { LL | | u_8 = u_8 - 1; @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:20:13 + --> $DIR/implicit_saturating_sub.rs:35:13 | LL | / if u_8 > 0 { LL | | u_8 -= 1; @@ -17,7 +17,7 @@ LL | | } | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:34:5 + --> $DIR/implicit_saturating_sub.rs:49:5 | LL | / if u_16 > 0 { LL | | u_16 -= 1; @@ -25,7 +25,7 @@ LL | | } | |_____^ help: try: `u_16 = u_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:44:5 + --> $DIR/implicit_saturating_sub.rs:59:5 | LL | / if u_32 != 0 { LL | | u_32 -= 1; @@ -33,7 +33,7 @@ LL | | } | |_____^ help: try: `u_32 = u_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:65:5 + --> $DIR/implicit_saturating_sub.rs:80:5 | LL | / if u_64 > 0 { LL | | u_64 -= 1; @@ -41,7 +41,7 @@ LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:70:5 + --> $DIR/implicit_saturating_sub.rs:85:5 | LL | / if 0 < u_64 { LL | | u_64 -= 1; @@ -49,7 +49,7 @@ LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:75:5 + --> $DIR/implicit_saturating_sub.rs:90:5 | LL | / if 0 != u_64 { LL | | u_64 -= 1; @@ -57,7 +57,7 @@ LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:96:5 + --> $DIR/implicit_saturating_sub.rs:111:5 | LL | / if u_usize > 0 { LL | | u_usize -= 1; @@ -65,7 +65,7 @@ LL | | } | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:108:5 + --> $DIR/implicit_saturating_sub.rs:123:5 | LL | / if i_8 > i8::MIN { LL | | i_8 -= 1; @@ -73,7 +73,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:113:5 + --> $DIR/implicit_saturating_sub.rs:128:5 | LL | / if i_8 > i8::MIN { LL | | i_8 -= 1; @@ -81,7 +81,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:118:5 + --> $DIR/implicit_saturating_sub.rs:133:5 | LL | / if i_8 != i8::MIN { LL | | i_8 -= 1; @@ -89,7 +89,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:123:5 + --> $DIR/implicit_saturating_sub.rs:138:5 | LL | / if i_8 != i8::MIN { LL | | i_8 -= 1; @@ -97,7 +97,7 @@ LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:133:5 + --> $DIR/implicit_saturating_sub.rs:148:5 | LL | / if i_16 > i16::MIN { LL | | i_16 -= 1; @@ -105,7 +105,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:138:5 + --> $DIR/implicit_saturating_sub.rs:153:5 | LL | / if i_16 > i16::MIN { LL | | i_16 -= 1; @@ -113,7 +113,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:143:5 + --> $DIR/implicit_saturating_sub.rs:158:5 | LL | / if i_16 != i16::MIN { LL | | i_16 -= 1; @@ -121,7 +121,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:148:5 + --> $DIR/implicit_saturating_sub.rs:163:5 | LL | / if i_16 != i16::MIN { LL | | i_16 -= 1; @@ -129,7 +129,7 @@ LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:158:5 + --> $DIR/implicit_saturating_sub.rs:173:5 | LL | / if i_32 > i32::MIN { LL | | i_32 -= 1; @@ -137,7 +137,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:163:5 + --> $DIR/implicit_saturating_sub.rs:178:5 | LL | / if i_32 > i32::MIN { LL | | i_32 -= 1; @@ -145,7 +145,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:168:5 + --> $DIR/implicit_saturating_sub.rs:183:5 | LL | / if i_32 != i32::MIN { LL | | i_32 -= 1; @@ -153,7 +153,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:173:5 + --> $DIR/implicit_saturating_sub.rs:188:5 | LL | / if i_32 != i32::MIN { LL | | i_32 -= 1; @@ -161,7 +161,7 @@ LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:183:5 + --> $DIR/implicit_saturating_sub.rs:198:5 | LL | / if i64::MIN < i_64 { LL | | i_64 -= 1; @@ -169,7 +169,7 @@ LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:188:5 + --> $DIR/implicit_saturating_sub.rs:203:5 | LL | / if i64::MIN != i_64 { LL | | i_64 -= 1; @@ -177,7 +177,7 @@ LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` error: implicitly performing saturating subtraction - --> $DIR/implicit_saturating_sub.rs:193:5 + --> $DIR/implicit_saturating_sub.rs:208:5 | LL | / if i64::MIN < i_64 { LL | | i_64 -= 1; diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index 0cadd5a3da19..1a646e49ce3a 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -40,3 +40,10 @@ fn main() { let ok26 = 0x6_A0_BF; let ok27 = 0b1_0010_0101; } + +fn issue9651() { + // lint but octal form is not possible here + let _ = 08; + let _ = 09; + let _ = 089; +} diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 365b24074735..603d47bacca8 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -135,5 +135,38 @@ error: digits of hex or binary literal not grouped by four LL | let fail25 = 0b01_100_101; | ^^^^^^^^^^^^ help: consider: `0b0110_0101` -error: aborting due to 18 previous errors +error: this is a decimal constant + --> $DIR/literals.rs:46:13 + | +LL | let _ = 08; + | ^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 8; + | ~ + +error: this is a decimal constant + --> $DIR/literals.rs:47:13 + | +LL | let _ = 09; + | ^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 9; + | ~ + +error: this is a decimal constant + --> $DIR/literals.rs:48:13 + | +LL | let _ = 089; + | ^^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 89; + | ~~ + +error: aborting due to 21 previous errors diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index 26e3b8f63e70..c9a819ba5354 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -1,6 +1,6 @@ // revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 // run-rustfix #![warn(clippy::manual_assert)] @@ -29,7 +29,9 @@ fn main() { panic!("qaqaq{:?}", a); } assert!(a.is_empty(), "qaqaq{:?}", a); - assert!(a.is_empty(), "qwqwq"); + if !a.is_empty() { + panic!("qwqwq"); + } if a.len() == 3 { println!("qwq"); println!("qwq"); @@ -44,21 +46,32 @@ fn main() { println!("qwq"); } let b = vec![1, 2, 3]; - assert!(!b.is_empty(), "panic1"); - assert!(!(b.is_empty() && a.is_empty()), "panic2"); - assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - assert!(!(b.is_empty() || a.is_empty()), "panic4"); - assert!(!(a.is_empty() || !b.is_empty()), "panic5"); + if b.is_empty() { + panic!("panic1"); + } + if b.is_empty() && a.is_empty() { + panic!("panic2"); + } + if a.is_empty() && !b.is_empty() { + panic!("panic3"); + } + if b.is_empty() || a.is_empty() { + panic!("panic4"); + } + if a.is_empty() || !b.is_empty() { + panic!("panic5"); + } assert!(!a.is_empty(), "with expansion {}", one!()); } fn issue7730(a: u8) { // Suggestion should preserve comment - // comment -/* this is a + if a > 2 { + // comment + /* this is a multiline comment */ -/// Doc comment -// comment after `panic!` -assert!(!(a > 2), "panic with comment"); + /// Doc comment + panic!("panic with comment") // comment after `panic!` + } } diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 237638ee1344..1f2e1e3087bd 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -8,54 +8,6 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:34:5 - | -LL | / if !a.is_empty() { -LL | | panic!("qwqwq"); -LL | | } - | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:51:5 - | -LL | / if b.is_empty() { -LL | | panic!("panic1"); -LL | | } - | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:54:5 - | -LL | / if b.is_empty() && a.is_empty() { -LL | | panic!("panic2"); -LL | | } - | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:57:5 - | -LL | / if a.is_empty() && !b.is_empty() { -LL | | panic!("panic3"); -LL | | } - | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:60:5 - | -LL | / if b.is_empty() || a.is_empty() { -LL | | panic!("panic4"); -LL | | } - | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` - -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:63:5 - | -LL | / if a.is_empty() || !b.is_empty() { -LL | | panic!("panic5"); -LL | | } - | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` - error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 | @@ -64,22 +16,5 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` -error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:73:5 - | -LL | / if a > 2 { -LL | | // comment -LL | | /* this is a -LL | | multiline -... | -LL | | panic!("panic with comment") // comment after `panic!` -LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a > 2), "panic with comment"); - | - -error: aborting due to 9 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed index 26e3b8f63e70..2f62de51cadc 100644 --- a/tests/ui/manual_assert.edition2021.fixed +++ b/tests/ui/manual_assert.edition2021.fixed @@ -1,6 +1,6 @@ // revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 // run-rustfix #![warn(clippy::manual_assert)] diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index 8c37753071df..6a4cc2468d41 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -1,6 +1,6 @@ // revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 // run-rustfix #![warn(clippy::manual_assert)] diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs index 54fd888af99f..331fd29b74e8 100644 --- a/tests/ui/manual_clamp.rs +++ b/tests/ui/manual_clamp.rs @@ -1,3 +1,4 @@ +#![feature(custom_inner_attributes)] #![warn(clippy::manual_clamp)] #![allow( unused, @@ -302,3 +303,29 @@ fn dont_tell_me_what_to_do() { fn cmp_min_max(input: i32) -> i32 { input * 3 } + +fn msrv_1_49() { + #![clippy::msrv = "1.49"] + + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} + +fn msrv_1_50() { + #![clippy::msrv = "1.50"] + + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr index 0604f8606c3f..70abe28091c9 100644 --- a/tests/ui/manual_clamp.stderr +++ b/tests/ui/manual_clamp.stderr @@ -1,5 +1,5 @@ error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:76:5 + --> $DIR/manual_clamp.rs:77:5 | LL | / if x9 < min { LL | | x9 = min; @@ -13,7 +13,7 @@ LL | | } = note: `-D clippy::manual-clamp` implied by `-D warnings` error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:91:5 + --> $DIR/manual_clamp.rs:92:5 | LL | / if x11 > max { LL | | x11 = max; @@ -26,7 +26,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:99:5 + --> $DIR/manual_clamp.rs:100:5 | LL | / if min > x12 { LL | | x12 = min; @@ -39,7 +39,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:107:5 + --> $DIR/manual_clamp.rs:108:5 | LL | / if max < x13 { LL | | x13 = max; @@ -52,7 +52,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:161:5 + --> $DIR/manual_clamp.rs:162:5 | LL | / if max < x33 { LL | | x33 = max; @@ -65,7 +65,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:21:14 + --> $DIR/manual_clamp.rs:22:14 | LL | let x0 = if max < input { | ______________^ @@ -80,7 +80,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:29:14 + --> $DIR/manual_clamp.rs:30:14 | LL | let x1 = if input > max { | ______________^ @@ -95,7 +95,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:37:14 + --> $DIR/manual_clamp.rs:38:14 | LL | let x2 = if input < min { | ______________^ @@ -110,7 +110,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:45:14 + --> $DIR/manual_clamp.rs:46:14 | LL | let x3 = if min > input { | ______________^ @@ -125,7 +125,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:53:14 + --> $DIR/manual_clamp.rs:54:14 | LL | let x4 = input.max(min).min(max); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -133,7 +133,7 @@ LL | let x4 = input.max(min).min(max); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:55:14 + --> $DIR/manual_clamp.rs:56:14 | LL | let x5 = input.min(max).max(min); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -141,7 +141,7 @@ LL | let x5 = input.min(max).max(min); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:57:14 + --> $DIR/manual_clamp.rs:58:14 | LL | let x6 = match input { | ______________^ @@ -154,7 +154,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:63:14 + --> $DIR/manual_clamp.rs:64:14 | LL | let x7 = match input { | ______________^ @@ -167,7 +167,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:69:14 + --> $DIR/manual_clamp.rs:70:14 | LL | let x8 = match input { | ______________^ @@ -180,7 +180,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:83:15 + --> $DIR/manual_clamp.rs:84:15 | LL | let x10 = match input { | _______________^ @@ -193,7 +193,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:114:15 + --> $DIR/manual_clamp.rs:115:15 | LL | let x14 = if input > CONST_MAX { | _______________^ @@ -208,7 +208,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:123:19 + --> $DIR/manual_clamp.rs:124:19 | LL | let x15 = if input > max { | ___________________^ @@ -224,7 +224,7 @@ LL | | }; = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:134:19 + --> $DIR/manual_clamp.rs:135:19 | LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -232,7 +232,7 @@ LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:135:19 + --> $DIR/manual_clamp.rs:136:19 | LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -240,7 +240,7 @@ LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:136:19 + --> $DIR/manual_clamp.rs:137:19 | LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -248,7 +248,7 @@ LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:137:19 + --> $DIR/manual_clamp.rs:138:19 | LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -256,7 +256,7 @@ LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:138:19 + --> $DIR/manual_clamp.rs:139:19 | LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -264,7 +264,7 @@ LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:139:19 + --> $DIR/manual_clamp.rs:140:19 | LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -272,7 +272,7 @@ LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:140:19 + --> $DIR/manual_clamp.rs:141:19 | LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -280,7 +280,7 @@ LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:141:19 + --> $DIR/manual_clamp.rs:142:19 | LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -288,7 +288,7 @@ LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:143:19 + --> $DIR/manual_clamp.rs:144:19 | LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -297,7 +297,7 @@ LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:144:19 + --> $DIR/manual_clamp.rs:145:19 | LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -306,7 +306,7 @@ LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:145:19 + --> $DIR/manual_clamp.rs:146:19 | LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -315,7 +315,7 @@ LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:146:19 + --> $DIR/manual_clamp.rs:147:19 | LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -324,7 +324,7 @@ LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:147:19 + --> $DIR/manual_clamp.rs:148:19 | LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -333,7 +333,7 @@ LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:148:19 + --> $DIR/manual_clamp.rs:149:19 | LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -342,7 +342,7 @@ LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:149:19 + --> $DIR/manual_clamp.rs:150:19 | LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -351,7 +351,7 @@ LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:150:19 + --> $DIR/manual_clamp.rs:151:19 | LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -360,7 +360,7 @@ LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:153:5 + --> $DIR/manual_clamp.rs:154:5 | LL | / if x32 < min { LL | | x32 = min; @@ -371,5 +371,20 @@ LL | | } | = note: clamp will panic if max < min -error: aborting due to 34 previous errors +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:324:13 + | +LL | let _ = if input < min { + | _____________^ +LL | | min +LL | | } else if input > max { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: aborting due to 35 previous errors diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed new file mode 100644 index 000000000000..3553291b87df --- /dev/null +++ b/tests/ui/manual_filter.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::manual_filter)] +#![allow(unused_variables, dead_code)] + +fn main() { + Some(0).filter(|&x| x <= 0); + + Some(1).filter(|&x| x <= 0); + + Some(2).filter(|&x| x <= 0); + + Some(3).filter(|&x| x > 0); + + let y = Some(4); + y.filter(|&x| x <= 0); + + Some(5).filter(|&x| x > 0); + + Some(6).as_ref().filter(|&x| x > &0); + + let external_cond = true; + Some(String::new()).filter(|x| external_cond); + + Some(7).filter(|&x| external_cond); + + Some(8).filter(|&x| x != 0); + + Some(9).filter(|&x| x > 10 && x < 100); + + const fn f1() { + // Don't lint, `.filter` is not const + match Some(10) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + } + + #[allow(clippy::blocks_in_if_conditions)] + Some(11).filter(|&x| { + println!("foo"); + x > 10 && x < 100 + }); + + match Some(12) { + // Don't Lint, statement is lost by `.filter` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(13) { + // Don't Lint, because of `None => Some(1)` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => Some(1), + }; + + unsafe fn f(x: u32) -> bool { + true + } + let _ = Some(14).filter(|&x| unsafe { f(x) }); + let _ = Some(15).filter(|&x| unsafe { f(x) }); + + #[allow(clippy::redundant_pattern_matching)] + if let Some(_) = Some(16) { + Some(16) + } else { Some(16).filter(|&x| x % 2 == 0) }; + + match Some((17, 17)) { + // Not linted for now could be + Some((x, y)) => { + if y != x { + Some((x, y)) + } else { + None + } + }, + None => None, + }; + + struct NamedTuple { + pub x: u8, + pub y: (i32, u32), + } + + match Some(NamedTuple { + // Not linted for now could be + x: 17, + y: (18, 19), + }) { + Some(NamedTuple { x, y }) => { + if y.1 != x as u32 { + Some(NamedTuple { x, y }) + } else { + None + } + }, + None => None, + }; +} diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs new file mode 100644 index 000000000000..aa9f90f752b1 --- /dev/null +++ b/tests/ui/manual_filter.rs @@ -0,0 +1,243 @@ +// run-rustfix + +#![warn(clippy::manual_filter)] +#![allow(unused_variables, dead_code)] + +fn main() { + match Some(0) { + None => None, + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + }; + + match Some(1) { + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + None => None, + }; + + match Some(2) { + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + _ => None, + }; + + match Some(3) { + Some(x) => { + if x > 0 { + Some(x) + } else { + None + } + }, + None => None, + }; + + let y = Some(4); + match y { + // Some(4) + None => None, + Some(x) => { + if x > 0 { + None + } else { + Some(x) + } + }, + }; + + match Some(5) { + Some(x) => { + if x > 0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + match Some(6) { + Some(ref x) => { + if x > &0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + let external_cond = true; + match Some(String::new()) { + Some(x) => { + if external_cond { + Some(x) + } else { + None + } + }, + _ => None, + }; + + if let Some(x) = Some(7) { + if external_cond { Some(x) } else { None } + } else { + None + }; + + match &Some(8) { + &Some(x) => { + if x != 0 { + Some(x) + } else { + None + } + }, + _ => None, + }; + + match Some(9) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + + const fn f1() { + // Don't lint, `.filter` is not const + match Some(10) { + Some(x) => { + if x > 10 && x < 100 { + Some(x) + } else { + None + } + }, + None => None, + }; + } + + #[allow(clippy::blocks_in_if_conditions)] + match Some(11) { + // Lint, statement is preserved by `.filter` + Some(x) => { + if { + println!("foo"); + x > 10 && x < 100 + } { + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(12) { + // Don't Lint, statement is lost by `.filter` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => None, + }; + + match Some(13) { + // Don't Lint, because of `None => Some(1)` + Some(x) => { + if x > 10 && x < 100 { + println!("foo"); + Some(x) + } else { + None + } + }, + None => Some(1), + }; + + unsafe fn f(x: u32) -> bool { + true + } + let _ = match Some(14) { + Some(x) => { + if unsafe { f(x) } { + Some(x) + } else { + None + } + }, + None => None, + }; + let _ = match Some(15) { + Some(x) => unsafe { + if f(x) { Some(x) } else { None } + }, + None => None, + }; + + #[allow(clippy::redundant_pattern_matching)] + if let Some(_) = Some(16) { + Some(16) + } else if let Some(x) = Some(16) { + // Lint starting from here + if x % 2 == 0 { Some(x) } else { None } + } else { + None + }; + + match Some((17, 17)) { + // Not linted for now could be + Some((x, y)) => { + if y != x { + Some((x, y)) + } else { + None + } + }, + None => None, + }; + + struct NamedTuple { + pub x: u8, + pub y: (i32, u32), + } + + match Some(NamedTuple { + // Not linted for now could be + x: 17, + y: (18, 19), + }) { + Some(NamedTuple { x, y }) => { + if y.1 != x as u32 { + Some(NamedTuple { x, y }) + } else { + None + } + }, + None => None, + }; +} diff --git a/tests/ui/manual_filter.stderr b/tests/ui/manual_filter.stderr new file mode 100644 index 000000000000..53dea9229306 --- /dev/null +++ b/tests/ui/manual_filter.stderr @@ -0,0 +1,191 @@ +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:7:5 + | +LL | / match Some(0) { +LL | | None => None, +LL | | Some(x) => { +LL | | if x > 0 { +... | +LL | | }, +LL | | }; + | |_____^ help: try this: `Some(0).filter(|&x| x <= 0)` + | + = note: `-D clippy::manual-filter` implied by `-D warnings` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:18:5 + | +LL | / match Some(1) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | None +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(1).filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:29:5 + | +LL | / match Some(2) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | None +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(2).filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:40:5 + | +LL | / match Some(3) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(3).filter(|&x| x > 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:52:5 + | +LL | / match y { +LL | | // Some(4) +LL | | None => None, +LL | | Some(x) => { +... | +LL | | }, +LL | | }; + | |_____^ help: try this: `y.filter(|&x| x <= 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:64:5 + | +LL | / match Some(5) { +LL | | Some(x) => { +LL | | if x > 0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(5).filter(|&x| x > 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:75:5 + | +LL | / match Some(6) { +LL | | Some(ref x) => { +LL | | if x > &0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(6).as_ref().filter(|&x| x > &0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:87:5 + | +LL | / match Some(String::new()) { +LL | | Some(x) => { +LL | | if external_cond { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).filter(|x| external_cond)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:98:5 + | +LL | / if let Some(x) = Some(7) { +LL | | if external_cond { Some(x) } else { None } +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `Some(7).filter(|&x| external_cond)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:104:5 + | +LL | / match &Some(8) { +LL | | &Some(x) => { +LL | | if x != 0 { +LL | | Some(x) +... | +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(8).filter(|&x| x != 0)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:115:5 + | +LL | / match Some(9) { +LL | | Some(x) => { +LL | | if x > 10 && x < 100 { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(9).filter(|&x| x > 10 && x < 100)` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:141:5 + | +LL | / match Some(11) { +LL | | // Lint, statement is preserved by `.filter` +LL | | Some(x) => { +LL | | if { +... | +LL | | None => None, +LL | | }; + | |_____^ + | +help: try this + | +LL ~ Some(11).filter(|&x| { +LL + println!("foo"); +LL + x > 10 && x < 100 +LL ~ }); + | + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:185:13 + | +LL | let _ = match Some(14) { + | _____________^ +LL | | Some(x) => { +LL | | if unsafe { f(x) } { +LL | | Some(x) +... | +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(14).filter(|&x| unsafe { f(x) })` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:195:13 + | +LL | let _ = match Some(15) { + | _____________^ +LL | | Some(x) => unsafe { +LL | | if f(x) { Some(x) } else { None } +LL | | }, +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(15).filter(|&x| unsafe { f(x) })` + +error: manual implementation of `Option::filter` + --> $DIR/manual_filter.rs:205:12 + | +LL | } else if let Some(x) = Some(16) { + | ____________^ +LL | | // Lint starting from here +LL | | if x % 2 == 0 { Some(x) } else { None } +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(16).filter(|&x| x % 2 == 0) }` + +error: aborting due to 15 previous errors + diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed index 5601c96c10b2..b942fbfe9305 100644 --- a/tests/ui/manual_rem_euclid.fixed +++ b/tests/ui/manual_rem_euclid.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -53,3 +54,32 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { num.rem_euclid(4) } + +pub fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub fn msrv_1_38() { + #![clippy::msrv = "1.38"] + + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} + +// For const fns: +pub const fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub const fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs index 52135be26b73..7462d532169f 100644 --- a/tests/ui/manual_rem_euclid.rs +++ b/tests/ui/manual_rem_euclid.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:macro_rules.rs +#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -53,3 +54,32 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { ((num % 4) + 4) % 4 } + +pub fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub fn msrv_1_38() { + #![clippy::msrv = "1.38"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +// For const fns: +pub const fn msrv_1_51() { + #![clippy::msrv = "1.51"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +pub const fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr index a237fd0213c1..d51bac03b565 100644 --- a/tests/ui/manual_rem_euclid.stderr +++ b/tests/ui/manual_rem_euclid.stderr @@ -1,5 +1,5 @@ error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:19:18 + --> $DIR/manual_rem_euclid.rs:20:18 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -7,31 +7,31 @@ LL | let _: i32 = ((value % 4) + 4) % 4; = note: `-D clippy::manual-rem-euclid` implied by `-D warnings` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:20:18 + --> $DIR/manual_rem_euclid.rs:21:18 | LL | let _: i32 = (4 + (value % 4)) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:21:18 + --> $DIR/manual_rem_euclid.rs:22:18 | LL | let _: i32 = (value % 4 + 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:22:18 + --> $DIR/manual_rem_euclid.rs:23:18 | LL | let _: i32 = (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:23:22 + --> $DIR/manual_rem_euclid.rs:24:22 | LL | let _: i32 = 1 + (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:12:22 + --> $DIR/manual_rem_euclid.rs:13:22 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -42,16 +42,28 @@ LL | internal_rem_euclid!(); = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:49:5 + --> $DIR/manual_rem_euclid.rs:50:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:54:5 + --> $DIR/manual_rem_euclid.rs:55:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` -error: aborting due to 8 previous errors +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:69:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:84:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index cbb84eb5c7e3..85009d78558b 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -1,3 +1,4 @@ +#![feature(custom_inner_attributes)] #![warn(clippy::manual_strip)] fn main() { @@ -64,3 +65,21 @@ fn main() { s4[2..].to_string(); } } + +fn msrv_1_44() { + #![clippy::msrv = "1.44"] + + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} + +fn msrv_1_45() { + #![clippy::msrv = "1.45"] + + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index 2191ccb85dd5..ad2a362f3e76 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> $DIR/manual_strip.rs:7:24 + --> $DIR/manual_strip.rs:8:24 | LL | str::to_string(&s["ab".len()..]); | ^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:6:5 + --> $DIR/manual_strip.rs:7:5 | LL | if s.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL ~ .to_string(); | error: stripping a suffix manually - --> $DIR/manual_strip.rs:15:24 + --> $DIR/manual_strip.rs:16:24 | LL | str::to_string(&s[..s.len() - "bc".len()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the suffix was tested here - --> $DIR/manual_strip.rs:14:5 + --> $DIR/manual_strip.rs:15:5 | LL | if s.ends_with("bc") { | ^^^^^^^^^^^^^^^^^^^^^ @@ -42,13 +42,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:24:24 + --> $DIR/manual_strip.rs:25:24 | LL | str::to_string(&s[1..]); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:23:5 + --> $DIR/manual_strip.rs:24:5 | LL | if s.starts_with('a') { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -60,13 +60,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:31:24 + --> $DIR/manual_strip.rs:32:24 | LL | str::to_string(&s[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:30:5 + --> $DIR/manual_strip.rs:31:5 | LL | if s.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,13 +77,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:37:24 + --> $DIR/manual_strip.rs:38:24 | LL | str::to_string(&s[PREFIX.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:36:5 + --> $DIR/manual_strip.rs:37:5 | LL | if s.starts_with(PREFIX) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,13 +95,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:44:24 + --> $DIR/manual_strip.rs:45:24 | LL | str::to_string(&TARGET[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:43:5 + --> $DIR/manual_strip.rs:44:5 | LL | if TARGET.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,13 +112,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:50:9 + --> $DIR/manual_strip.rs:51:9 | LL | s1[2..].to_uppercase(); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:49:5 + --> $DIR/manual_strip.rs:50:5 | LL | if s1.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,5 +128,22 @@ LL ~ if let Some() = s1.strip_prefix("ab") { LL ~ .to_uppercase(); | -error: aborting due to 7 previous errors +error: stripping a prefix manually + --> $DIR/manual_strip.rs:83:9 + | +LL | s[1..].to_string(); + | ^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:82:5 + | +LL | if s.starts_with('a') { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL ~ if let Some() = s.strip_prefix('a') { +LL ~ .to_string(); + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 5429fb4e454e..396b22a9abb3 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,8 @@ // aux-build:option_helpers.rs + +#![feature(custom_inner_attributes)] #![warn(clippy::map_unwrap_or)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)] #[macro_use] extern crate option_helpers; @@ -79,3 +81,19 @@ fn main() { option_methods(); result_methods(); } + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index abc9c1ece327..d17d24a403ea 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,5 +1,5 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:16:13 + --> $DIR/map_unwrap_or.rs:18:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -15,7 +15,7 @@ LL + let _ = opt.map_or(0, |x| x + 1); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:20:13 + --> $DIR/map_unwrap_or.rs:22:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -33,7 +33,7 @@ LL ~ ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:24:13 + --> $DIR/map_unwrap_or.rs:26:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -50,7 +50,7 @@ LL ~ }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:29:13 + --> $DIR/map_unwrap_or.rs:31:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + let _ = opt.and_then(|x| Some(x + 1)); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:31:13 + --> $DIR/map_unwrap_or.rs:33:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -80,7 +80,7 @@ LL ~ ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:35:13 + --> $DIR/map_unwrap_or.rs:37:13 | LL | let _ = opt | _____________^ @@ -95,7 +95,7 @@ LL + .and_then(|x| Some(x + 1)); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:46:13 + --> $DIR/map_unwrap_or.rs:48:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:50:13 + --> $DIR/map_unwrap_or.rs:52:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -117,7 +117,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:54:13 + --> $DIR/map_unwrap_or.rs:56:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -127,7 +127,7 @@ LL | | ); | |_________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:66:13 + --> $DIR/map_unwrap_or.rs:68:13 | LL | let _ = res.map(|x| { | _____________^ @@ -137,7 +137,7 @@ LL | | ).unwrap_or_else(|_e| 0); | |____________________________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:70:13 + --> $DIR/map_unwrap_or.rs:72:13 | LL | let _ = res.map(|x| x + 1) | _____________^ @@ -146,5 +146,11 @@ LL | | 0 LL | | }); | |__________^ -error: aborting due to 11 previous errors +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:98:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: aborting due to 12 previous errors diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 95ca571d07bf..2498007694c5 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] @@ -193,3 +194,18 @@ fn main() { _ => false, }; } + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn msrv_1_42() { + #![clippy::msrv = "1.42"] + + let _y = matches!(Some(5), Some(0)); +} diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 3b9c8cadadcc..b4e48499bd0f 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] @@ -234,3 +235,21 @@ fn main() { _ => false, }; } + +fn msrv_1_41() { + #![clippy::msrv = "1.41"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn msrv_1_42() { + #![clippy::msrv = "1.42"] + + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index e94555e27448..f1d1c23aeb0d 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:10:14 + --> $DIR/match_expr_like_matches_macro.rs:11:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:16:14 + --> $DIR/match_expr_like_matches_macro.rs:17:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:22:14 + --> $DIR/match_expr_like_matches_macro.rs:23:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:28:15 + --> $DIR/match_expr_like_matches_macro.rs:29:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:34:16 + --> $DIR/match_expr_like_matches_macro.rs:35:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:58:20 + --> $DIR/match_expr_like_matches_macro.rs:59:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:68:20 + --> $DIR/match_expr_like_matches_macro.rs:69:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:78:20 + --> $DIR/match_expr_like_matches_macro.rs:79:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:138:18 + --> $DIR/match_expr_like_matches_macro.rs:139:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:147:18 + --> $DIR/match_expr_like_matches_macro.rs:148:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:164:21 + --> $DIR/match_expr_like_matches_macro.rs:165:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:178:20 + --> $DIR/match_expr_like_matches_macro.rs:179:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:190:20 + --> $DIR/match_expr_like_matches_macro.rs:191:20 | LL | let _res = match &val { | ____________________^ @@ -133,5 +133,15 @@ LL | | _ => false, LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` -error: aborting due to 13 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:251:14 + | +LL | let _y = match Some(5) { + | ______________^ +LL | | Some(0) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(Some(5), Some(0))` + +error: aborting due to 14 previous errors diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 22b04b208f87..b4097fa96045 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -1,5 +1,4 @@ #![feature(exclusive_range_pattern)] - #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let)] diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index a72becbeb669..b98d4799e42c 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -1,96 +1,96 @@ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:13:9 + --> $DIR/match_overlapping_arm.rs:12:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:14:9 + --> $DIR/match_overlapping_arm.rs:13:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ = note: `-D clippy::match-overlapping-arm` implied by `-D warnings` error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:19:9 + --> $DIR/match_overlapping_arm.rs:18:9 | LL | 0..=5 => println!("0..=5"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:21:9 + --> $DIR/match_overlapping_arm.rs:20:9 | LL | FOO..=11 => println!("FOO..=11"), | ^^^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:56:9 + --> $DIR/match_overlapping_arm.rs:55:9 | LL | 0..11 => println!("0..11"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:57:9 + --> $DIR/match_overlapping_arm.rs:56:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:81:9 + --> $DIR/match_overlapping_arm.rs:80:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:80:9 + --> $DIR/match_overlapping_arm.rs:79:9 | LL | 5..14 => println!("5..14"), | ^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:86:9 + --> $DIR/match_overlapping_arm.rs:85:9 | LL | 0..7 => println!("0..7"), | ^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:87:9 + --> $DIR/match_overlapping_arm.rs:86:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:98:9 + --> $DIR/match_overlapping_arm.rs:97:9 | LL | ..=23 => println!("..=23"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:99:9 + --> $DIR/match_overlapping_arm.rs:98:9 | LL | ..26 => println!("..26"), | ^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:107:9 + --> $DIR/match_overlapping_arm.rs:106:9 | LL | 21..=30 => (), | ^^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:108:9 + --> $DIR/match_overlapping_arm.rs:107:9 | LL | 21..=40 => (), | ^^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:121:9 + --> $DIR/match_overlapping_arm.rs:120:9 | LL | 0..=0x0000_0000_0000_00ff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:122:9 + --> $DIR/match_overlapping_arm.rs:121:9 | LL | 0..=0x0000_0000_0000_ffff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 951f552eb32b..a6e315e4773a 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -124,3 +124,12 @@ fn issue_8723() { let _ = val; } + +#[allow(dead_code)] +fn issue_9575() { + fn side_effects() {} + let _ = || { + side_effects(); + println!("Needs curlies"); + }; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 19c0fee8fd68..cecbd703e566 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -140,3 +140,11 @@ fn issue_8723() { let _ = val; } + +#[allow(dead_code)] +fn issue_9575() { + fn side_effects() {} + let _ = || match side_effects() { + _ => println!("Needs curlies"), + }; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 5d4e7314b213..2b9ec7ee7026 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -196,5 +196,22 @@ LL + suf LL ~ }; | -error: aborting due to 13 previous errors +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding.rs:147:16 + | +LL | let _ = || match side_effects() { + | ________________^ +LL | | _ => println!("Needs curlies"), +LL | | }; + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ let _ = || { +LL + side_effects(); +LL + println!("Needs curlies"); +LL ~ }; + | + +error: aborting due to 14 previous errors diff --git a/tests/ui/match_wild_err_arm.edition2021.stderr b/tests/ui/match_wild_err_arm.edition2021.stderr deleted file mode 100644 index 525533bf07bb..000000000000 --- a/tests/ui/match_wild_err_arm.edition2021.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:14:9 - | -LL | Err(_) => panic!("err"), - | ^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` - -error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:20:9 - | -LL | Err(_) => panic!(), - | ^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - -error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:26:9 - | -LL | Err(_) => { - | ^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - -error: `Err(_e)` matches all errors - --> $DIR/match_wild_err_arm.rs:34:9 - | -LL | Err(_e) => panic!(), - | ^^^^^^^ - | - = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable - -error: aborting due to 4 previous errors - diff --git a/tests/ui/match_wild_err_arm.rs b/tests/ui/match_wild_err_arm.rs index 0a86144b95d5..823be65efe06 100644 --- a/tests/ui/match_wild_err_arm.rs +++ b/tests/ui/match_wild_err_arm.rs @@ -1,6 +1,3 @@ -// revisions: edition2018 edition2021 -// [edition2018] edition:2018 -// [edition2021] edition:2021 #![feature(exclusive_range_pattern)] #![allow(clippy::match_same_arms)] #![warn(clippy::match_wild_err_arm)] diff --git a/tests/ui/match_wild_err_arm.edition2018.stderr b/tests/ui/match_wild_err_arm.stderr similarity index 86% rename from tests/ui/match_wild_err_arm.edition2018.stderr rename to tests/ui/match_wild_err_arm.stderr index 525533bf07bb..b016d682698c 100644 --- a/tests/ui/match_wild_err_arm.edition2018.stderr +++ b/tests/ui/match_wild_err_arm.stderr @@ -1,5 +1,5 @@ error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:14:9 + --> $DIR/match_wild_err_arm.rs:11:9 | LL | Err(_) => panic!("err"), | ^^^^^^ @@ -8,7 +8,7 @@ LL | Err(_) => panic!("err"), = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:20:9 + --> $DIR/match_wild_err_arm.rs:17:9 | LL | Err(_) => panic!(), | ^^^^^^ @@ -16,7 +16,7 @@ LL | Err(_) => panic!(), = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:26:9 + --> $DIR/match_wild_err_arm.rs:23:9 | LL | Err(_) => { | ^^^^^^ @@ -24,7 +24,7 @@ LL | Err(_) => { = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_e)` matches all errors - --> $DIR/match_wild_err_arm.rs:34:9 + --> $DIR/match_wild_err_arm.rs:31:9 | LL | Err(_e) => panic!(), | ^^^^^^^ diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index b609ba659467..ae237395b95f 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -1,5 +1,7 @@ // run-rustfix -#![allow(unused_imports)] + +#![feature(custom_inner_attributes)] +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +79,17 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let mut s = String::from("foo"); + let _ = std::mem::take(&mut s); +} diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 93f6dcdec83b..3202e99e0be9 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -1,5 +1,7 @@ // run-rustfix -#![allow(unused_imports)] + +#![feature(custom_inner_attributes)] +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +79,17 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index 90dc6c95f858..dd8a50dab900 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:15:13 + --> $DIR/mem_replace.rs:17:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:17:13 + --> $DIR/mem_replace.rs:19:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:22:13 + --> $DIR/mem_replace.rs:24:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,100 +21,106 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:25:13 + --> $DIR/mem_replace.rs:27:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:26:13 + --> $DIR/mem_replace.rs:28:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:29:13 + --> $DIR/mem_replace.rs:31:13 | LL | let _ = std::mem::replace(&mut v, Vec::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:30:13 + --> $DIR/mem_replace.rs:32:13 | LL | let _ = std::mem::replace(&mut v, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:31:13 + --> $DIR/mem_replace.rs:33:13 | LL | let _ = std::mem::replace(&mut v, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:32:13 + --> $DIR/mem_replace.rs:34:13 | LL | let _ = std::mem::replace(&mut v, vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:35:13 + --> $DIR/mem_replace.rs:37:13 | LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:38:13 + --> $DIR/mem_replace.rs:40:13 | LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:41:13 + --> $DIR/mem_replace.rs:43:13 | LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:44:13 + --> $DIR/mem_replace.rs:46:13 | LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:47:13 + --> $DIR/mem_replace.rs:49:13 | LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:50:13 + --> $DIR/mem_replace.rs:52:13 | LL | let _ = std::mem::replace(&mut list, LinkedList::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:53:13 + --> $DIR/mem_replace.rs:55:13 | LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:56:13 + --> $DIR/mem_replace.rs:58:13 | LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:59:13 + --> $DIR/mem_replace.rs:61:13 | LL | let _ = std::mem::replace(&mut refstr, ""); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:62:13 + --> $DIR/mem_replace.rs:64:13 | LL | let _ = std::mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` -error: aborting due to 19 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:94:13 + | +LL | let _ = std::mem::replace(&mut s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` + +error: aborting due to 20 previous errors diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index c4c6391bb4c1..cd148063bf06 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -1,240 +1,29 @@ #![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0.0"] -use std::ops::{Deref, RangeFrom}; +fn main() {} -fn approx_const() { +fn just_under_msrv() { + #![clippy::msrv = "1.42.0"] let log2_10 = 3.321928094887362; - let log10_2 = 0.301029995663981; } -fn cloned_instead_of_copied() { - let _ = [1].iter().cloned(); -} - -fn option_as_ref_deref() { - let mut opt = Some(String::from("123")); - - let _ = opt.as_ref().map(String::as_str); - let _ = opt.as_ref().map(|x| x.as_str()); - let _ = opt.as_mut().map(String::as_mut_str); - let _ = opt.as_mut().map(|x| x.as_mut_str()); -} - -fn match_like_matches() { - let _y = match Some(5) { - Some(0) => true, - _ => false, - }; -} - -fn match_same_arms() { - match (1, 2, 3) { - (1, .., 3) => 42, - (.., 3) => 42, //~ ERROR match arms have same body - _ => 0, - }; -} - -fn match_same_arms2() { - let _ = match Some(42) { - Some(_) => 24, - None => 24, //~ ERROR match arms have same body - }; -} - -pub fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -pub fn redundant_fieldnames() { - let start = 0; - let _ = RangeFrom { start: start }; -} - -pub fn redundant_static_lifetime() { - const VAR_ONE: &'static str = "Test constant #1"; -} - -pub fn checked_conversion() { - let value: i64 = 42; - let _ = value <= (u32::max_value() as i64) && value >= 0; - let _ = value <= (u32::MAX as i64) && value >= 0; -} - -pub struct FromOverInto(String); - -impl Into for String { - fn into(self) -> FromOverInto { - FromOverInto(self) - } -} - -pub fn filter_map_next() { - let a = ["1", "lol", "3", "NaN", "5"]; - - #[rustfmt::skip] - let _: Option = vec![1, 2, 3, 4, 5, 6] - .into_iter() - .filter_map(|x| { - if x == 2 { - Some(x * 2) - } else { - None - } - }) - .next(); -} - -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] -pub fn manual_range_contains() { - let x = 5; - x >= 8 && x < 12; -} - -pub fn use_self() { - struct Foo; - - impl Foo { - fn new() -> Foo { - Foo {} - } - fn test() -> Foo { - Foo::new() - } - } -} - -fn replace_with_default() { - let mut s = String::from("foo"); - let _ = std::mem::replace(&mut s, String::default()); -} - -fn map_unwrap_or() { - let opt = Some(1); - - // Check for `option.map(_).unwrap_or(_)` use. - // Single line case. - let _ = opt - .map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or(0); -} - -// Could be const -fn missing_const_for_fn() -> i32 { - 1 -} - -fn unnest_or_patterns() { - struct TS(u8, u8); - if let TS(0, x) | TS(1, x) = TS(0, 0) {} -} - -#[cfg_attr(rustfmt, rustfmt_skip)] -fn deprecated_cfg_attr() {} - -#[warn(clippy::cast_lossless)] -fn int_from_bool() -> u8 { - true as u8 -} - -fn err_expect() { - let x: Result = Ok(10); - x.err().expect("Testing expect_err"); -} - -fn cast_abs_to_unsigned() { - let x: i32 = 10; - assert_eq!(10u32, x.abs() as u32); -} - -fn manual_rem_euclid() { - let x: i32 = 10; - let _: i32 = ((x % 4) + 4) % 4; -} - -fn manual_clamp() { - let (input, min, max) = (0, -1, 2); - let _ = if input < min { - min - } else if input > max { - max - } else { - input - }; -} - -fn main() { - filter_map_next(); - checked_conversion(); - redundant_fieldnames(); - redundant_static_lifetime(); - option_as_ref_deref(); - match_like_matches(); - match_same_arms(); - match_same_arms2(); - manual_strip_msrv(); - manual_range_contains(); - use_self(); - replace_with_default(); - map_unwrap_or(); - missing_const_for_fn(); - unnest_or_patterns(); - int_from_bool(); - err_expect(); - cast_abs_to_unsigned(); - manual_rem_euclid(); - manual_clamp(); +fn meets_msrv() { + #![clippy::msrv = "1.43.0"] + let log2_10 = 3.321928094887362; } -mod just_under_msrv { - #![feature(custom_inner_attributes)] +fn just_above_msrv() { #![clippy::msrv = "1.44.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } -} - -mod meets_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.45.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } + let log2_10 = 3.321928094887362; } -mod just_above_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.46.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } +fn no_patch_under() { + #![clippy::msrv = "1.42"] + let log2_10 = 3.321928094887362; } -mod const_rem_euclid { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.50.0"] - - pub const fn const_rem_euclid_4(num: i32) -> i32 { - ((num % 4) + 4) % 4 - } +fn no_patch_meets() { + #![clippy::msrv = "1.43"] + let log2_10 = 3.321928094887362; } diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index d1cffc26a831..68aa58748190 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,37 +1,27 @@ -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:216:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:13:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:215:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `-D clippy::manual-strip` implied by `-D warnings` -help: try using the `strip_prefix` method - | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly + = note: `#[deny(clippy::approx_constant)]` on by default -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:228:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:18:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:227:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:28:19 | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs index f20841891a74..02892f329af6 100644 --- a/tests/ui/min_rust_version_invalid_attr.rs +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -2,3 +2,17 @@ #![clippy::msrv = "invalid.version"] fn main() {} + +#[clippy::msrv = "invalid.version"] +fn outer_attr() {} + +mod multiple { + #![clippy::msrv = "1.40"] + #![clippy::msrv = "=1.35.0"] + #![clippy::msrv = "1.10.1"] + + mod foo { + #![clippy::msrv = "1"] + #![clippy::msrv = "1.0.0"] + } +} diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr index 6ff88ca56f8b..93370a0fa9c9 100644 --- a/tests/ui/min_rust_version_invalid_attr.stderr +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -4,5 +4,47 @@ error: `invalid.version` is not a valid Rust version LL | #![clippy::msrv = "invalid.version"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: `msrv` cannot be an outer attribute + --> $DIR/min_rust_version_invalid_attr.rs:6:1 + | +LL | #[clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:11:5 + | +LL | #![clippy::msrv = "=1.35.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:12:5 + | +LL | #![clippy::msrv = "1.10.1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:16:9 + | +LL | #![clippy::msrv = "1.0.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:15:9 + | +LL | #![clippy::msrv = "1"] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/min_rust_version_multiple_inner_attr.rs b/tests/ui/min_rust_version_multiple_inner_attr.rs deleted file mode 100644 index e882d5ccf91a..000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.40"] -#![clippy::msrv = "=1.35.0"] -#![clippy::msrv = "1.10.1"] - -mod foo { - #![clippy::msrv = "1"] - #![clippy::msrv = "1.0.0"] -} - -fn main() {} diff --git a/tests/ui/min_rust_version_multiple_inner_attr.stderr b/tests/ui/min_rust_version_multiple_inner_attr.stderr deleted file mode 100644 index e3ff6605cde8..000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:3:1 - | -LL | #![clippy::msrv = "=1.35.0"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 - | -LL | #![clippy::msrv = "1.40"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:4:1 - | -LL | #![clippy::msrv = "1.10.1"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 - | -LL | #![clippy::msrv = "1.40"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:8:5 - | -LL | #![clippy::msrv = "1.0.0"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:7:5 - | -LL | #![clippy::msrv = "1"] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs deleted file mode 100644 index 98fffe1e3512..000000000000 --- a/tests/ui/min_rust_version_no_patch.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![allow(clippy::redundant_clone)] -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0"] - -fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -fn main() { - manual_strip_msrv() -} diff --git a/tests/ui/min_rust_version_outer_attr.rs b/tests/ui/min_rust_version_outer_attr.rs deleted file mode 100644 index 551948bd72ef..000000000000 --- a/tests/ui/min_rust_version_outer_attr.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![feature(custom_inner_attributes)] - -#[clippy::msrv = "invalid.version"] -fn main() {} diff --git a/tests/ui/min_rust_version_outer_attr.stderr b/tests/ui/min_rust_version_outer_attr.stderr deleted file mode 100644 index 579ee7a87d23..000000000000 --- a/tests/ui/min_rust_version_outer_attr.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: `msrv` cannot be an outer attribute - --> $DIR/min_rust_version_outer_attr.rs:3:1 - | -LL | #[clippy::msrv = "invalid.version"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 88f6935d224a..b85e88784918 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -77,5 +77,17 @@ mod const_fn_stabilized_before_msrv { } } +fn msrv_1_45() -> i32 { + #![clippy::msrv = "1.45"] + + 45 +} + +fn msrv_1_46() -> i32 { + #![clippy::msrv = "1.46"] + + 46 +} + // Should not be const fn main() {} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 3eb52b682747..f8e221c82f1a 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -81,5 +81,15 @@ LL | | byte.is_ascii_digit(); LL | | } | |_____^ -error: aborting due to 10 previous errors +error: this could be a `const fn` + --> $DIR/could_be_const.rs:86:1 + | +LL | / fn msrv_1_46() -> i32 { +LL | | #![clippy::msrv = "1.46"] +LL | | +LL | | 46 +LL | | } + | |_^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/missing_trait_methods.rs b/tests/ui/missing_trait_methods.rs new file mode 100644 index 000000000000..8df885919a3e --- /dev/null +++ b/tests/ui/missing_trait_methods.rs @@ -0,0 +1,50 @@ +#![allow(unused, clippy::needless_lifetimes)] +#![warn(clippy::missing_trait_methods)] + +trait A { + fn provided() {} +} + +trait B { + fn required(); + + fn a(_: usize) -> usize { + 1 + } + + fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { + a.as_ref() + } +} + +struct Partial; + +impl A for Partial {} + +impl B for Partial { + fn required() {} + + fn a(_: usize) -> usize { + 2 + } +} + +struct Complete; + +impl A for Complete { + fn provided() {} +} + +impl B for Complete { + fn required() {} + + fn a(_: usize) -> usize { + 2 + } + + fn b>(a: &T) -> &[u8] { + a.as_ref() + } +} + +fn main() {} diff --git a/tests/ui/missing_trait_methods.stderr b/tests/ui/missing_trait_methods.stderr new file mode 100644 index 000000000000..0c5205e19657 --- /dev/null +++ b/tests/ui/missing_trait_methods.stderr @@ -0,0 +1,27 @@ +error: missing trait method provided by default: `provided` + --> $DIR/missing_trait_methods.rs:22:1 + | +LL | impl A for Partial {} + | ^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> $DIR/missing_trait_methods.rs:5:5 + | +LL | fn provided() {} + | ^^^^^^^^^^^^^ + = note: `-D clippy::missing-trait-methods` implied by `-D warnings` + +error: missing trait method provided by default: `b` + --> $DIR/missing_trait_methods.rs:24:1 + | +LL | impl B for Partial { + | ^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> $DIR/missing_trait_methods.rs:15:5 + | +LL | fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index aa2687159ef4..340e89d2db1d 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -3,7 +3,11 @@ #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables)] -#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] +#[allow( + clippy::uninlined_format_args, + clippy::unnecessary_mut_passed, + clippy::unnecessary_to_owned +)] fn main() { let a = 5; let ref_a = &a; @@ -134,6 +138,7 @@ fn main() { multiple_constraints([[""]]); multiple_constraints_normalizes_to_same(X, X); let _ = Some("").unwrap_or(""); + let _ = std::fs::write("x", "".to_string()); only_sized(&""); // Don't lint. `Sized` is only bound let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound @@ -276,8 +281,9 @@ mod copyable_iterator { fn dont_warn(mut x: Iter) { takes_iter(&mut x); } + #[allow(unused_mut)] fn warn(mut x: &mut Iter) { - takes_iter(&mut x) + takes_iter(x) } } @@ -327,3 +333,55 @@ fn issue9383() { ManuallyDrop::drop(&mut ocean.coral); } } + +#[allow(dead_code)] +fn closure_test() { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", arg); + let _ = std::fs::write("x", loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); +} + +#[allow(dead_code)] +mod significant_drop { + #[derive(Debug)] + struct X; + + #[derive(Debug)] + struct Y; + + impl Drop for Y { + fn drop(&mut self) {} + } + + fn foo(x: X, y: Y) { + debug(x); + debug(&y); // Don't lint. Has significant drop + } + + fn debug(_: impl std::fmt::Debug) {} +} + +#[allow(dead_code)] +mod used_exactly_once { + fn foo(x: String) { + use_x(x); + } + fn use_x(_: impl AsRef) {} +} + +#[allow(dead_code)] +mod used_more_than_once { + fn foo(x: String) { + use_x(&x); + use_x_again(&x); + } + fn use_x(_: impl AsRef) {} + fn use_x_again(_: impl AsRef) {} +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index d41251e8f6aa..c93711ac8e28 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -3,7 +3,11 @@ #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables)] -#[allow(clippy::uninlined_format_args, clippy::unnecessary_mut_passed)] +#[allow( + clippy::uninlined_format_args, + clippy::unnecessary_mut_passed, + clippy::unnecessary_to_owned +)] fn main() { let a = 5; let ref_a = &a; @@ -134,6 +138,7 @@ fn main() { multiple_constraints(&[[""]]); multiple_constraints_normalizes_to_same(&X, X); let _ = Some("").unwrap_or(&""); + let _ = std::fs::write("x", &"".to_string()); only_sized(&""); // Don't lint. `Sized` is only bound let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound @@ -276,6 +281,7 @@ mod copyable_iterator { fn dont_warn(mut x: Iter) { takes_iter(&mut x); } + #[allow(unused_mut)] fn warn(mut x: &mut Iter) { takes_iter(&mut x) } @@ -327,3 +333,55 @@ fn issue9383() { ManuallyDrop::drop(&mut ocean.coral); } } + +#[allow(dead_code)] +fn closure_test() { + let env = "env".to_owned(); + let arg = "arg".to_owned(); + let f = |arg| { + let loc = "loc".to_owned(); + let _ = std::fs::write("x", &env); // Don't lint. In environment + let _ = std::fs::write("x", &arg); + let _ = std::fs::write("x", &loc); + }; + let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f` + f(arg); +} + +#[allow(dead_code)] +mod significant_drop { + #[derive(Debug)] + struct X; + + #[derive(Debug)] + struct Y; + + impl Drop for Y { + fn drop(&mut self) {} + } + + fn foo(x: X, y: Y) { + debug(&x); + debug(&y); // Don't lint. Has significant drop + } + + fn debug(_: impl std::fmt::Debug) {} +} + +#[allow(dead_code)] +mod used_exactly_once { + fn foo(x: String) { + use_x(&x); + } + fn use_x(_: impl AsRef) {} +} + +#[allow(dead_code)] +mod used_more_than_once { + fn foo(x: String) { + use_x(&x); + use_x_again(&x); + } + fn use_x(_: impl AsRef) {} + fn use_x_again(_: impl AsRef) {} +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 5af68706d4ba..8b593268bec2 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,5 +1,5 @@ error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:11:15 + --> $DIR/needless_borrow.rs:15:15 | LL | let _ = x(&&a); // warn | ^^^ help: change this to: `&a` @@ -7,172 +7,208 @@ LL | let _ = x(&&a); // warn = note: `-D clippy::needless-borrow` implied by `-D warnings` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:15:13 + --> $DIR/needless_borrow.rs:19:13 | LL | mut_ref(&mut &mut b); // warn | ^^^^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:27:13 + --> $DIR/needless_borrow.rs:31:13 | LL | &&a | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:29:15 + --> $DIR/needless_borrow.rs:33:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:35:27 + --> $DIR/needless_borrow.rs:39:27 | LL | break &ref_a; | ^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:42:15 + --> $DIR/needless_borrow.rs:46:15 | LL | let _ = x(&&&a); | ^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:43:15 + --> $DIR/needless_borrow.rs:47:15 | LL | let _ = x(&mut &&a); | ^^^^^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:44:15 + --> $DIR/needless_borrow.rs:48:15 | LL | let _ = x(&&&mut b); | ^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:45:15 + --> $DIR/needless_borrow.rs:49:15 | LL | let _ = x(&&ref_a); | ^^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:48:11 + --> $DIR/needless_borrow.rs:52:11 | LL | x(&b); | ^^ help: change this to: `b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:55:13 + --> $DIR/needless_borrow.rs:59:13 | LL | mut_ref(&mut x); | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:56:13 + --> $DIR/needless_borrow.rs:60:13 | LL | mut_ref(&mut &mut x); | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:57:23 + --> $DIR/needless_borrow.rs:61:23 | LL | let y: &mut i32 = &mut x; | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:58:23 + --> $DIR/needless_borrow.rs:62:23 | LL | let y: &mut i32 = &mut &mut x; | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:67:14 + --> $DIR/needless_borrow.rs:71:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:73:14 + --> $DIR/needless_borrow.rs:77:14 | LL | 0 => &mut x, | ^^^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:85:13 + --> $DIR/needless_borrow.rs:89:13 | LL | let _ = (&x).0; | ^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:87:22 + --> $DIR/needless_borrow.rs:91:22 | LL | let _ = unsafe { (&*x).0 }; | ^^^^^ help: change this to: `(*x)` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:97:5 + --> $DIR/needless_borrow.rs:101:5 | LL | (&&()).foo(); | ^^^^^^ help: change this to: `(&())` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:106:5 + --> $DIR/needless_borrow.rs:110:5 | LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:131:51 + --> $DIR/needless_borrow.rs:135:51 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:132:44 + --> $DIR/needless_borrow.rs:136:44 | LL | let _ = std::path::Path::new(".").join(&&"."); | ^^^^^ help: change this to: `"."` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:133:23 + --> $DIR/needless_borrow.rs:137:23 | LL | deref_target_is_x(&X); | ^^ help: change this to: `X` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:134:26 + --> $DIR/needless_borrow.rs:138:26 | LL | multiple_constraints(&[[""]]); | ^^^^^^^ help: change this to: `[[""]]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:135:45 + --> $DIR/needless_borrow.rs:139:45 | LL | multiple_constraints_normalizes_to_same(&X, X); | ^^ help: change this to: `X` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:136:32 + --> $DIR/needless_borrow.rs:140:32 | LL | let _ = Some("").unwrap_or(&""); | ^^^ help: change this to: `""` +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:141:33 + | +LL | let _ = std::fs::write("x", &"".to_string()); + | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` + error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:187:13 + --> $DIR/needless_borrow.rs:192:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:196:13 + --> $DIR/needless_borrow.rs:201:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:298:55 + --> $DIR/needless_borrow.rs:286:20 + | +LL | takes_iter(&mut x) + | ^^^^^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:304:55 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` -error: aborting due to 29 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:344:37 + | +LL | let _ = std::fs::write("x", &arg); + | ^^^^ help: change this to: `arg` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:345:37 + | +LL | let _ = std::fs::write("x", &loc); + | ^^^^ help: change this to: `loc` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:364:15 + | +LL | debug(&x); + | ^^ help: change this to: `x` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:374:15 + | +LL | use_x(&x); + | ^^ help: change this to: `x` + +error: aborting due to 35 previous errors diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index 07d7f0b45b0c..bc376d0d7fb3 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -42,3 +43,17 @@ fn main() { // Issue #5927 let _ = opt.as_deref(); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let opt = Some(String::from("123")); + let _ = opt.as_deref(); +} diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index 6ae059c9425d..ba3a2eedc225 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -1,6 +1,7 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -45,3 +46,17 @@ fn main() { // Issue #5927 let _ = opt.as_ref().map(std::ops::Deref::deref); } + +fn msrv_1_39() { + #![clippy::msrv = "1.39"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +fn msrv_1_40() { + #![clippy::msrv = "1.40"] + + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index 62f282324752..7de8b3b6ba43 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -1,5 +1,5 @@ error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:13:13 + --> $DIR/option_as_ref_deref.rs:14:13 | LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` @@ -7,7 +7,7 @@ LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:16:13 + --> $DIR/option_as_ref_deref.rs:17:13 | LL | let _ = opt.clone() | _____________^ @@ -17,94 +17,100 @@ LL | | ) | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:22:13 + --> $DIR/option_as_ref_deref.rs:23:13 | LL | let _ = opt.as_mut().map(DerefMut::deref_mut); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:24:13 + --> $DIR/option_as_ref_deref.rs:25:13 | LL | let _ = opt.as_ref().map(String::as_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:25:13 + --> $DIR/option_as_ref_deref.rs:26:13 | LL | let _ = opt.as_ref().map(|x| x.as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:26:13 + --> $DIR/option_as_ref_deref.rs:27:13 | LL | let _ = opt.as_mut().map(String::as_mut_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:27:13 + --> $DIR/option_as_ref_deref.rs:28:13 | LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:28:13 + --> $DIR/option_as_ref_deref.rs:29:13 | LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:29:13 + --> $DIR/option_as_ref_deref.rs:30:13 | LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:30:13 + --> $DIR/option_as_ref_deref.rs:31:13 | LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:31:13 + --> $DIR/option_as_ref_deref.rs:32:13 | LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:32:13 + --> $DIR/option_as_ref_deref.rs:33:13 | LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:34:13 + --> $DIR/option_as_ref_deref.rs:35:13 | LL | let _ = opt.as_ref().map(|x| x.deref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:35:13 + --> $DIR/option_as_ref_deref.rs:36:13 | LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:42:13 + --> $DIR/option_as_ref_deref.rs:43:13 | LL | let _ = opt.as_ref().map(|x| &**x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:43:13 + --> $DIR/option_as_ref_deref.rs:44:13 | LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:46:13 + --> $DIR/option_as_ref_deref.rs:47:13 | LL | let _ = opt.as_ref().map(std::ops::Deref::deref); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` -error: aborting due to 17 previous errors +error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:61:13 + | +LL | let _ = opt.as_ref().map(String::as_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 896430780ea8..23b1aa8bebd5 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -225,4 +225,15 @@ mod issue8239 { } } +mod issue9608 { + fn sig_drop() { + enum X { + X(std::fs::File), + Y(u32), + } + + let _ = None.unwrap_or(X::Y(0)); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 2473163d4fd2..039998f22dd7 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -225,4 +225,15 @@ mod issue8239 { } } +mod issue9608 { + fn sig_drop() { + enum X { + X(std::fs::File), + Y(u32), + } + + let _ = None.unwrap_or(X::Y(0)); + } +} + fn main() {} diff --git a/tests/ui/partial_pub_fields.rs b/tests/ui/partial_pub_fields.rs new file mode 100644 index 000000000000..668545da8441 --- /dev/null +++ b/tests/ui/partial_pub_fields.rs @@ -0,0 +1,40 @@ +#![allow(unused)] +#![warn(clippy::partial_pub_fields)] + +fn main() { + use std::collections::HashMap; + + #[derive(Default)] + pub struct FileSet { + files: HashMap, + pub paths: HashMap, + } + + pub struct Color { + pub r: u8, + pub g: u8, + b: u8, + } + + pub struct Point(i32, pub i32); + + pub struct Visibility { + r#pub: bool, + pub pos: u32, + } + + // Don't lint on empty structs; + pub struct Empty1; + pub struct Empty2(); + pub struct Empty3 {}; + + // Don't lint on structs with one field. + pub struct Single1(i32); + pub struct Single2(pub i32); + pub struct Single3 { + v1: i32, + } + pub struct Single4 { + pub v1: i32, + } +} diff --git a/tests/ui/partial_pub_fields.stderr b/tests/ui/partial_pub_fields.stderr new file mode 100644 index 000000000000..84cfc1a91940 --- /dev/null +++ b/tests/ui/partial_pub_fields.stderr @@ -0,0 +1,35 @@ +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:10:9 + | +LL | pub paths: HashMap, + | ^^^ + | + = help: consider using private field here + = note: `-D clippy::partial-pub-fields` implied by `-D warnings` + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:16:9 + | +LL | b: u8, + | ^ + | + = help: consider using public field here + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:19:27 + | +LL | pub struct Point(i32, pub i32); + | ^^^ + | + = help: consider using private field here + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:23:9 + | +LL | pub pos: u32, + | ^^^ + | + = help: consider using private field here + +error: aborting due to 4 previous errors + diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index fd15001e540c..5f54101ca15a 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -3,7 +3,7 @@ #![warn(clippy::ptr_arg)] use std::borrow::Cow; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; fn do_vec(x: &Vec) { //Nothing here @@ -207,3 +207,31 @@ fn cow_conditional_to_mut(a: &mut Cow) { a.to_mut().push_str("foo"); } } + +// Issue #9542 +fn dyn_trait_ok(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + trait T {} + impl T for Vec {} + impl T for String {} + impl T for PathBuf {} + fn takes_dyn(_: &mut dyn T) {} + + takes_dyn(a); + takes_dyn(b); + takes_dyn(c); +} + +fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + trait T {} + impl T for Vec {} + impl T for [U] {} + impl T for String {} + impl T for str {} + impl T for PathBuf {} + impl T for Path {} + fn takes_dyn(_: &mut dyn T) {} + + takes_dyn(a); + takes_dyn(b); + takes_dyn(c); +} diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index d64b5f454a5a..6b4de98ce88c 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -162,5 +162,23 @@ error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a sl LL | fn mut_vec_slice_methods(v: &mut Vec) { | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` -error: aborting due to 17 previous errors +error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:17 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` + +error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:35 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^ help: change this to: `&mut str` + +error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:51 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^^ help: change this to: `&mut Path` + +error: aborting due to 20 previous errors diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 85d021b2f25e..824f00cb99e8 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -1,10 +1,12 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_range_contains)] +#![allow(unused)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::unnecessary_operation)] + fn main() { let x = 9_i32; @@ -62,3 +64,17 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let x = 5; + x >= 8 && x < 34; +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let x = 5; + (8..35).contains(&x); +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 9a7a75dc1325..df925eeadfe5 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -1,10 +1,12 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_range_contains)] +#![allow(unused)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::unnecessary_operation)] + fn main() { let x = 9_i32; @@ -62,3 +64,17 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +fn msrv_1_34() { + #![clippy::msrv = "1.34"] + + let x = 5; + x >= 8 && x < 34; +} + +fn msrv_1_35() { + #![clippy::msrv = "1.35"] + + let x = 5; + x >= 8 && x < 35; +} diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 936859db5a12..9689e665b05c 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -1,5 +1,5 @@ error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:12:5 + --> $DIR/range_contains.rs:14:5 | LL | x >= 8 && x < 12; | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` @@ -7,118 +7,124 @@ LL | x >= 8 && x < 12; = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:13:5 + --> $DIR/range_contains.rs:15:5 | LL | x < 42 && x >= 21; | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:14:5 + --> $DIR/range_contains.rs:16:5 | LL | 100 > x && 1 <= x; | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:17:5 + --> $DIR/range_contains.rs:19:5 | LL | x >= 9 && x <= 99; | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:18:5 + --> $DIR/range_contains.rs:20:5 | LL | x <= 33 && x >= 1; | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:19:5 + --> $DIR/range_contains.rs:21:5 | LL | 999 >= x && 1 <= x; | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:22:5 + --> $DIR/range_contains.rs:24:5 | LL | x < 8 || x >= 12; | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:23:5 + --> $DIR/range_contains.rs:25:5 | LL | x >= 42 || x < 21; | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:24:5 + --> $DIR/range_contains.rs:26:5 | LL | 100 <= x || 1 > x; | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:27:5 + --> $DIR/range_contains.rs:29:5 | LL | x < 9 || x > 99; | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:28:5 + --> $DIR/range_contains.rs:30:5 | LL | x > 33 || x < 1; | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:29:5 + --> $DIR/range_contains.rs:31:5 | LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:44:5 + --> $DIR/range_contains.rs:46:5 | LL | y >= 0. && y < 1.; | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:45:5 + --> $DIR/range_contains.rs:47:5 | LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:48:5 + --> $DIR/range_contains.rs:50:5 | LL | x >= -10 && x <= 10; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:50:5 + --> $DIR/range_contains.rs:52:5 | LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:30 + --> $DIR/range_contains.rs:57:30 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:5 + --> $DIR/range_contains.rs:57:5 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:29 + --> $DIR/range_contains.rs:58:29 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:5 + --> $DIR/range_contains.rs:58:5 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` -error: aborting due to 20 previous errors +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:79:5 + | +LL | x >= 8 && x < 35; + | ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)` + +error: aborting due to 21 previous errors diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index 5b4b8eeedd46..34ab552cb1d8 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,4 +1,6 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -69,3 +71,17 @@ fn issue_3476() { S { foo: foo:: }; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let start = 0; + let _ = RangeFrom { start: start }; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let start = 0; + let _ = RangeFrom { start }; +} diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 3f97b80c5682..a051b1f96f0f 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,4 +1,6 @@ // run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -69,3 +71,17 @@ fn issue_3476() { S { foo: foo:: }; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + let start = 0; + let _ = RangeFrom { start: start }; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + let start = 0; + let _ = RangeFrom { start: start }; +} diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 7976292df224..8b82e062b93a 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -1,5 +1,5 @@ error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:34:9 + --> $DIR/redundant_field_names.rs:36:9 | LL | gender: gender, | ^^^^^^^^^^^^^^ help: replace it with: `gender` @@ -7,40 +7,46 @@ LL | gender: gender, = note: `-D clippy::redundant-field-names` implied by `-D warnings` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:35:9 + --> $DIR/redundant_field_names.rs:37:9 | LL | age: age, | ^^^^^^^^ help: replace it with: `age` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:56:25 + --> $DIR/redundant_field_names.rs:58:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:57:23 + --> $DIR/redundant_field_names.rs:59:23 | LL | let _ = RangeTo { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:21 + --> $DIR/redundant_field_names.rs:60:21 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:35 + --> $DIR/redundant_field_names.rs:60:35 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:60:32 + --> $DIR/redundant_field_names.rs:62:32 | LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` -error: aborting due to 7 previous errors +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:86:25 + | +LL | let _ = RangeFrom { start: start }; + | ^^^^^^^^^^^^ help: replace it with: `start` + +error: aborting due to 8 previous errors diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index acc8f1e25b6e..42110dbe81e8 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -54,3 +55,15 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + static V: &'static u8 = &16; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + static V: &u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index f2f0f78659c9..bc5200bc8625 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -54,3 +55,15 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +fn msrv_1_16() { + #![clippy::msrv = "1.16"] + + static V: &'static u8 = &16; +} + +fn msrv_1_17() { + #![clippy::msrv = "1.17"] + + static V: &'static u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 649831f9c069..735113460d28 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -1,5 +1,5 @@ error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:8:17 + --> $DIR/redundant_static_lifetimes.rs:9:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` @@ -7,94 +7,100 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:12:21 + --> $DIR/redundant_static_lifetimes.rs:13:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:14:32 + --> $DIR/redundant_static_lifetimes.rs:15:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:14:47 + --> $DIR/redundant_static_lifetimes.rs:15:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:16:17 + --> $DIR/redundant_static_lifetimes.rs:17:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:18:20 + --> $DIR/redundant_static_lifetimes.rs:19:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:20:19 + --> $DIR/redundant_static_lifetimes.rs:21:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:22:19 + --> $DIR/redundant_static_lifetimes.rs:23:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:24:19 + --> $DIR/redundant_static_lifetimes.rs:25:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:26:25 + --> $DIR/redundant_static_lifetimes.rs:27:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:30:29 + --> $DIR/redundant_static_lifetimes.rs:31:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:32:25 + --> $DIR/redundant_static_lifetimes.rs:33:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:34:28 + --> $DIR/redundant_static_lifetimes.rs:35:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:36:27 + --> $DIR/redundant_static_lifetimes.rs:37:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:38:27 + --> $DIR/redundant_static_lifetimes.rs:39:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:40:27 + --> $DIR/redundant_static_lifetimes.rs:41:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` -error: aborting due to 16 previous errors +error: statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:68:16 + | +LL | static V: &'static u8 = &17; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: aborting due to 17 previous errors diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 2df45c927d71..e487799e1522 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -45,3 +45,8 @@ impl RefOptTrait for u32 { fn main() { let x: &Option<&u32> = &None; } + +fn issue9682(arg: &Option<&mut String>) { + // Should not lint, as the inner ref is mutable making it non `Copy` + println!("{arg:?}"); +} diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index 3ca7a4019025..106274479751 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -150,6 +150,19 @@ fn tester(fn_arg: i32) { println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); println!("{}", with_span!(span val)); + + if local_i32 > 0 { + panic!("p1 {local_i32}"); + } + if local_i32 > 0 { + panic!("p2 {local_i32}"); + } + if local_i32 > 0 { + panic!("p3 {local_i32}"); + } + if local_i32 > 0 { + panic!("p4 {local_i32}"); + } } fn main() { diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs index 924191f4324c..8e495ebd083a 100644 --- a/tests/ui/uninlined_format_args.rs +++ b/tests/ui/uninlined_format_args.rs @@ -150,6 +150,19 @@ fn tester(fn_arg: i32) { println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); println!("{}", with_span!(span val)); + + if local_i32 > 0 { + panic!("p1 {}", local_i32); + } + if local_i32 > 0 { + panic!("p2 {0}", local_i32); + } + if local_i32 > 0 { + panic!("p3 {local_i32}", local_i32 = local_i32); + } + if local_i32 > 0 { + panic!("p4 {local_i32}"); + } } fn main() { diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index d1a774926342..2ce3b7fa960c 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -828,7 +828,43 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:168:5 + --> $DIR/uninlined_format_args.rs:155:9 + | +LL | panic!("p1 {}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p1 {}", local_i32); +LL + panic!("p1 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:158:9 + | +LL | panic!("p2 {0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p2 {0}", local_i32); +LL + panic!("p2 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:161:9 + | +LL | panic!("p3 {local_i32}", local_i32 = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p3 {local_i32}", local_i32 = local_i32); +LL + panic!("p3 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:181:5 | LL | println!("expand='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -839,5 +875,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 70 previous errors +error: aborting due to 73 previous errors diff --git a/tests/ui/uninlined_format_args_panic.edition2018.fixed b/tests/ui/uninlined_format_args_panic.edition2018.fixed new file mode 100644 index 000000000000..96cc0877960e --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2018.fixed @@ -0,0 +1,29 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix + +#![warn(clippy::uninlined_format_args)] + +fn main() { + let var = 1; + + println!("val='{var}'"); + + if var > 0 { + panic!("p1 {}", var); + } + if var > 0 { + panic!("p2 {0}", var); + } + if var > 0 { + panic!("p3 {var}", var = var); + } + + #[allow(non_fmt_panics)] + { + if var > 0 { + panic!("p4 {var}"); + } + } +} diff --git a/tests/ui/uninlined_format_args_panic.edition2018.stderr b/tests/ui/uninlined_format_args_panic.edition2018.stderr new file mode 100644 index 000000000000..2c8061259229 --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2018.stderr @@ -0,0 +1,15 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:11:5 + | +LL | println!("val='{}'", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", var); +LL + println!("val='{var}'"); + | + +error: aborting due to previous error + diff --git a/tests/ui/uninlined_format_args_panic.edition2021.fixed b/tests/ui/uninlined_format_args_panic.edition2021.fixed new file mode 100644 index 000000000000..faf8ca4d3a79 --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2021.fixed @@ -0,0 +1,29 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix + +#![warn(clippy::uninlined_format_args)] + +fn main() { + let var = 1; + + println!("val='{var}'"); + + if var > 0 { + panic!("p1 {var}"); + } + if var > 0 { + panic!("p2 {var}"); + } + if var > 0 { + panic!("p3 {var}"); + } + + #[allow(non_fmt_panics)] + { + if var > 0 { + panic!("p4 {var}"); + } + } +} diff --git a/tests/ui/uninlined_format_args_panic.edition2021.stderr b/tests/ui/uninlined_format_args_panic.edition2021.stderr new file mode 100644 index 000000000000..0f09c45f4132 --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.edition2021.stderr @@ -0,0 +1,51 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:11:5 + | +LL | println!("val='{}'", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", var); +LL + println!("val='{var}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:14:9 + | +LL | panic!("p1 {}", var); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p1 {}", var); +LL + panic!("p1 {var}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:17:9 + | +LL | panic!("p2 {0}", var); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p2 {0}", var); +LL + panic!("p2 {var}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:20:9 + | +LL | panic!("p3 {var}", var = var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p3 {var}", var = var); +LL + panic!("p3 {var}"); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/uninlined_format_args_panic.rs b/tests/ui/uninlined_format_args_panic.rs new file mode 100644 index 000000000000..6421c5bbed2f --- /dev/null +++ b/tests/ui/uninlined_format_args_panic.rs @@ -0,0 +1,29 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix + +#![warn(clippy::uninlined_format_args)] + +fn main() { + let var = 1; + + println!("val='{}'", var); + + if var > 0 { + panic!("p1 {}", var); + } + if var > 0 { + panic!("p2 {0}", var); + } + if var > 0 { + panic!("p3 {var}", var = var); + } + + #[allow(non_fmt_panics)] + { + if var > 0 { + panic!("p4 {var}"); + } + } +} diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 94dc96427263..ec8c6abfab91 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -111,4 +111,8 @@ mod fixable { let _num = foo(); } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index e5150256f69a..5213cdc269bd 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -111,4 +111,8 @@ mod fixable { let _num = foo() as f32; } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index f97583aa22f9..fe09aad06bc8 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] #![feature(custom_inner_attributes)] diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index aa5394a56579..3de6d0903c0f 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::ptr_arg)] +#![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] #![feature(custom_inner_attributes)] diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index c223b5bc711b..9786c7b12128 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -1,9 +1,9 @@ // run-rustfix -#![feature(box_patterns)] +#![feature(box_patterns, custom_inner_attributes)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,15 @@ fn main() { if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + if let [1] | [52] = [0] {} +} + +fn msrv_1_53() { + #![clippy::msrv = "1.53"] + + if let [1 | 53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 04cd11036e4e..f57322396d4a 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -1,9 +1,9 @@ // run-rustfix -#![feature(box_patterns)] +#![feature(box_patterns, custom_inner_attributes)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,15 @@ fn main() { if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +fn msrv_1_52() { + #![clippy::msrv = "1.52"] + + if let [1] | [52] = [0] {} +} + +fn msrv_1_53() { + #![clippy::msrv = "1.53"] + + if let [1] | [53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index 453c66cbba8f..fbc12fff0b0e 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -175,5 +175,16 @@ help: nest the patterns LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | ~~~~~~~~~~~~~~~~~ -error: aborting due to 16 previous errors +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:46:12 + | +LL | if let [1] | [53] = [0] {} + | ^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let [1 | 53] = [0] {} + | ~~~~~~~~ + +error: aborting due to 17 previous errors diff --git a/tests/ui/unused_format_specs.fixed b/tests/ui/unused_format_specs.fixed new file mode 100644 index 000000000000..2930722b42d9 --- /dev/null +++ b/tests/ui/unused_format_specs.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +fn main() { + let f = 1.0f64; + println!("{}", 1.0); + println!("{f} {f:?}"); + + println!("{}", 1); +} + +fn should_not_lint() { + let f = 1.0f64; + println!("{:.1}", 1.0); + println!("{f:.w$} {f:.*?}", 3, w = 2); +} diff --git a/tests/ui/unused_format_specs.rs b/tests/ui/unused_format_specs.rs new file mode 100644 index 000000000000..ee192a000d4b --- /dev/null +++ b/tests/ui/unused_format_specs.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +fn main() { + let f = 1.0f64; + println!("{:.}", 1.0); + println!("{f:.} {f:.?}"); + + println!("{:.}", 1); +} + +fn should_not_lint() { + let f = 1.0f64; + println!("{:.1}", 1.0); + println!("{f:.w$} {f:.*?}", 3, w = 2); +} diff --git a/tests/ui/unused_format_specs.stderr b/tests/ui/unused_format_specs.stderr new file mode 100644 index 000000000000..7231c17e74c1 --- /dev/null +++ b/tests/ui/unused_format_specs.stderr @@ -0,0 +1,54 @@ +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:8:17 + | +LL | println!("{:.}", 1.0); + | ^ + | + = note: a precision specifier is not required to format floats + = note: `-D clippy::unused-format-specs` implied by `-D warnings` +help: remove the `.` + | +LL - println!("{:.}", 1.0); +LL + println!("{}", 1.0); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:9:18 + | +LL | println!("{f:.} {f:.?}"); + | ^ + | + = note: a precision specifier is not required to format floats +help: remove the `.` + | +LL - println!("{f:.} {f:.?}"); +LL + println!("{f} {f:.?}"); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:9:24 + | +LL | println!("{f:.} {f:.?}"); + | ^ + | + = note: a precision specifier is not required to format floats +help: remove the `.` + | +LL - println!("{f:.} {f:.?}"); +LL + println!("{f:.} {f:?}"); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:11:17 + | +LL | println!("{:.}", 1); + | ^ + | +help: remove the `.` + | +LL - println!("{:.}", 1); +LL + println!("{}", 1); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unused_format_specs_unfixable.rs b/tests/ui/unused_format_specs_unfixable.rs new file mode 100644 index 000000000000..78601a3483d3 --- /dev/null +++ b/tests/ui/unused_format_specs_unfixable.rs @@ -0,0 +1,30 @@ +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +macro_rules! format_args_from_macro { + () => { + format_args!("from macro") + }; +} + +fn main() { + // prints `.`, not ` .` + println!("{:5}.", format_args!("")); + //prints `abcde`, not `abc` + println!("{:.3}", format_args!("abcde")); + + println!("{:5}.", format_args_from_macro!()); + + let args = format_args!(""); + println!("{args:5}"); +} + +fn should_not_lint() { + println!("{}", format_args!("")); + // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use + // debug formatting so allow it + println!("{:?}", format_args!("")); + + let args = format_args!(""); + println!("{args}"); +} diff --git a/tests/ui/unused_format_specs_unfixable.stderr b/tests/ui/unused_format_specs_unfixable.stderr new file mode 100644 index 000000000000..9f1890282e6a --- /dev/null +++ b/tests/ui/unused_format_specs_unfixable.stderr @@ -0,0 +1,69 @@ +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:12:15 + | +LL | println!("{:5}.", format_args!("")); + | ^^^^ + | + = note: `-D clippy::unused-format-specs` implied by `-D warnings` +help: for the width to apply consider using `format!()` + | +LL | println!("{:5}.", format!("")); + | ~~~~~~ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:5}.", format_args!("")); +LL + println!("{}.", format_args!("")); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:14:15 + | +LL | println!("{:.3}", format_args!("abcde")); + | ^^^^^ + | +help: for the precision to apply consider using `format!()` + | +LL | println!("{:.3}", format!("abcde")); + | ~~~~~~ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:.3}", format_args!("abcde")); +LL + println!("{}", format_args!("abcde")); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:16:15 + | +LL | println!("{:5}.", format_args_from_macro!()); + | ^^^^ + | +help: for the width to apply consider using `format!()` + --> $DIR/unused_format_specs_unfixable.rs:16:17 + | +LL | println!("{:5}.", format_args_from_macro!()); + | ^ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:5}.", format_args_from_macro!()); +LL + println!("{}.", format_args_from_macro!()); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:19:15 + | +LL | println!("{args:5}"); + | ^^^^^^^^ + | +help: for the width to apply consider using `format!()` + --> $DIR/unused_format_specs_unfixable.rs:19:21 + | +LL | println!("{args:5}"); + | ^ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{args:5}"); +LL + println!("{args}"); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 37986187da17..3b54fe9d5ff3 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -617,3 +618,35 @@ mod issue6902 { Bar = 1, } } + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + Self::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 1b2b3337c92e..bf87633cd2d8 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -617,3 +618,35 @@ mod issue6902 { Bar = 1, } } + +fn msrv_1_36() { + #![clippy::msrv = "1.36"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +fn msrv_1_37() { + #![clippy::msrv = "1.37"] + + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index f06bb959b3bd..16fb0609242c 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:22:21 + --> $DIR/use_self.rs:23:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -7,244 +7,250 @@ LL | fn new() -> Foo { = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:13 + --> $DIR/use_self.rs:24:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:25:22 + --> $DIR/use_self.rs:26:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:26:13 + --> $DIR/use_self.rs:27:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:31:25 + --> $DIR/use_self.rs:32:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:32:13 + --> $DIR/use_self.rs:33:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:97:24 + --> $DIR/use_self.rs:98:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:97:55 + --> $DIR/use_self.rs:98:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:112:13 + --> $DIR/use_self.rs:113:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:147:29 + --> $DIR/use_self.rs:148:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:21 + --> $DIR/use_self.rs:149:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:159:21 + --> $DIR/use_self.rs:160:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:160:13 + --> $DIR/use_self.rs:161:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:177:21 + --> $DIR/use_self.rs:178:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:178:21 + --> $DIR/use_self.rs:179:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:179:21 + --> $DIR/use_self.rs:180:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:221:13 + --> $DIR/use_self.rs:222:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:222:13 + --> $DIR/use_self.rs:223:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:224:13 + --> $DIR/use_self.rs:225:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:243:13 + --> $DIR/use_self.rs:244:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:257:25 + --> $DIR/use_self.rs:258:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:13 + --> $DIR/use_self.rs:259:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:262:16 + --> $DIR/use_self.rs:263:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:262:22 + --> $DIR/use_self.rs:263:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:285:29 + --> $DIR/use_self.rs:286:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:286:13 + --> $DIR/use_self.rs:287:13 | LL | Foo:: { value } | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:458:13 + --> $DIR/use_self.rs:459:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:495:13 + --> $DIR/use_self.rs:496:13 | LL | S2::new() | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:532:17 + --> $DIR/use_self.rs:533:17 | LL | Foo::Bar => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:533:17 + --> $DIR/use_self.rs:534:17 | LL | Foo::Baz => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:539:20 + --> $DIR/use_self.rs:540:20 | LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:563:17 + --> $DIR/use_self.rs:564:17 | LL | Something::Num(n) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:564:17 + --> $DIR/use_self.rs:565:17 | LL | Something::TupleNums(n, _m) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:565:17 + --> $DIR/use_self.rs:566:17 | LL | Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:571:17 + --> $DIR/use_self.rs:572:17 | LL | crate::issue8845::Something::Num(n) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:572:17 + --> $DIR/use_self.rs:573:17 | LL | crate::issue8845::Something::TupleNums(n, _m) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:573:17 + --> $DIR/use_self.rs:574:17 | LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:589:17 + --> $DIR/use_self.rs:590:17 | LL | let Foo(x) = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:594:17 + --> $DIR/use_self.rs:595:17 | LL | let crate::issue8845::Foo(x) = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:601:17 + --> $DIR/use_self.rs:602:17 | LL | let Bar { x, .. } = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:606:17 + --> $DIR/use_self.rs:607:17 | LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 41 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:648:17 + | +LL | E::A => {}, + | ^ help: use the applicable keyword: `Self` + +error: aborting due to 42 previous errors diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 9e07769a8e4f..a6d8d0307ce5 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -48,7 +48,7 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`. let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string()); let rustc_version = String::from_utf8( - std::process::Command::new(&rustc) + std::process::Command::new(rustc) .arg("--version") .output() .expect("failed to run `rustc --version`") diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index c5d602ea3035..e46ad2c6e0ee 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -442,6 +442,12 @@

Clippy Lints

All +
  • + +
  • Citizen - Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their - definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. -* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or - made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation - team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a - safe place for you and we've got your back. -* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. - -## Moderation - - -These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, -please contact the [Rust moderation team][mod_team]. - -1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, - are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) -2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. -3. Moderators will first respond to such remarks with a warning. -4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. -5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. -6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended - party a genuine apology. -7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a - different moderator, **in private**. Complaints about bans in-channel are not allowed. -8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate - situation, they should expect less leeway than others. - -In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically -unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly -if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can -drive people away from the community entirely. - -And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was -they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good -there was something you could've communicated better — remember that it's your responsibility to make your fellow -Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about -cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their -trust. - -The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, -#rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); -GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org -(users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the -maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider -explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. - -*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the -[Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* - -[mod_team]: https://www.rust-lang.org/team.html#Moderation-team +The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). From e4540ad65fa14ceebd5145ab771fe4918d170bf1 Mon Sep 17 00:00:00 2001 From: koka Date: Mon, 24 Oct 2022 23:49:59 +0900 Subject: [PATCH 144/524] feat: implement manual_is_ascii_check lint modify fix: allow unused in test code fix: types in doc comment Update clippy_lints/src/manual_is_ascii_check.rs Co-authored-by: Fridtjof Stoldt Update clippy_lints/src/manual_is_ascii_check.rs Co-authored-by: Fridtjof Stoldt Update clippy_lints/src/manual_is_ascii_check.rs Co-authored-by: Fridtjof Stoldt fix ui test result fix: unnecessary format! chore: apply feedbacks * check msrvs also for const fn * check applicability manually * modify documents --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/manual_is_ascii_check.rs | 157 ++++++++++++++++++++++ clippy_utils/src/msrvs.rs | 2 +- tests/ui/manual_is_ascii_check.fixed | 45 +++++++ tests/ui/manual_is_ascii_check.rs | 45 +++++++ tests/ui/manual_is_ascii_check.stderr | 70 ++++++++++ 8 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/manual_is_ascii_check.rs create mode 100644 tests/ui/manual_is_ascii_check.fixed create mode 100644 tests/ui/manual_is_ascii_check.rs create mode 100644 tests/ui/manual_is_ascii_check.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 35864e85673a..d75ef52e0ee4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3997,6 +3997,7 @@ Released 2018-09-13 [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed +[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index f4fc2bd33309..448b12f1b0a0 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -251,6 +251,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_bits::MANUAL_BITS_INFO, crate::manual_clamp::MANUAL_CLAMP_INFO, crate::manual_instant_elapsed::MANUAL_INSTANT_ELAPSED_INFO, + crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 197d8f2dc081..fe05f2a13f9a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -173,6 +173,7 @@ mod manual_async_fn; mod manual_bits; mod manual_clamp; mod manual_instant_elapsed; +mod manual_is_ascii_check; mod manual_let_else; mod manual_non_exhaustive; mod manual_rem_euclid; @@ -918,6 +919,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); + store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs new file mode 100644 index 000000000000..3a6b693f7662 --- /dev/null +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -0,0 +1,157 @@ +use rustc_ast::LitKind::{Byte, Char}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{def_id::DefId, sym}; + +use clippy_utils::{ + diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet, +}; + +declare_clippy_lint! { + /// ### What it does + /// Suggests to use dedicated built-in methods, + /// `is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range + /// + /// ### Why is this bad? + /// Using the built-in functions is more readable and makes it + /// clear that it's not a specific subset of characters, but all + /// ASCII (lowercase|uppercase|digit) characters. + /// ### Example + /// ```rust + /// fn main() { + /// assert!(matches!('x', 'a'..='z')); + /// assert!(matches!(b'X', b'A'..=b'Z')); + /// assert!(matches!('2', '0'..='9')); + /// assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// assert!('x'.is_ascii_lowercase()); + /// assert!(b'X'.is_ascii_uppercase()); + /// assert!('2'.is_ascii_digit()); + /// assert!('x'.is_ascii_alphabetic()); + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub MANUAL_IS_ASCII_CHECK, + style, + "use dedicated method to check ascii range" +} +impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]); + +pub struct ManualIsAsciiCheck { + msrv: Option, +} + +impl ManualIsAsciiCheck { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +#[derive(Debug, PartialEq)] +enum CharRange { + /// 'a'..='z' | b'a'..=b'z' + LowerChar, + /// 'A'..='Z' | b'A'..=b'Z' + UpperChar, + /// AsciiLower | AsciiUpper + FullChar, + /// '0..=9' + Digit, + Otherwise, +} + +impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) { + return; + } + + if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) { + return; + } + + let Some(macro_call) = root_macro_call(expr.span) else { return }; + + if is_matches_macro(cx, macro_call.def_id) { + if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { + let range = check_pat(&arm.pat.kind); + + if let Some(sugg) = match range { + CharRange::UpperChar => Some("is_ascii_uppercase"), + CharRange::LowerChar => Some("is_ascii_lowercase"), + CharRange::FullChar => Some("is_ascii_alphabetic"), + CharRange::Digit => Some("is_ascii_digit"), + CharRange::Otherwise => None, + } { + let mut applicability = Applicability::MaybeIncorrect; + let default_snip = ".."; + // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for + // macro span, so we check applicability manually by comaring `recv` is not default. + let recv = snippet(cx, recv.span, default_snip); + + if recv != default_snip { + applicability = Applicability::MachineApplicable; + } + + span_lint_and_sugg( + cx, + MANUAL_IS_ASCII_CHECK, + macro_call.span, + "manual check for common ascii range", + "try", + format!("{recv}.{sugg}()"), + applicability, + ); + } + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { + match pat_kind { + PatKind::Or(pats) => { + let ranges = pats.iter().map(|p| check_pat(&p.kind)).collect::>(); + + if ranges.len() == 2 && ranges.contains(&CharRange::UpperChar) && ranges.contains(&CharRange::LowerChar) { + CharRange::FullChar + } else { + CharRange::Otherwise + } + }, + PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end), + _ => CharRange::Otherwise, + } +} + +fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { + if let ExprKind::Lit(start_lit) = &start.kind + && let ExprKind::Lit(end_lit) = &end.kind { + match (&start_lit.node, &end_lit.node) { + (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, + (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, + _ => CharRange::Otherwise, + } + } else { + CharRange::Otherwise + } +} + +fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { + if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) { + return sym::matches_macro == name; + } + + false +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 9a672e2ddfd7..79b19e6fb3eb 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -19,7 +19,7 @@ msrv_aliases! { 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS } 1,50,0 { BOOL_THEN, CLAMP } - 1,47,0 { TAU } + 1,47,0 { TAU, IS_ASCII_DIGIT_CONST } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } 1,43,0 { LOG2_10, LOG10_2 } diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed new file mode 100644 index 000000000000..765bb785994e --- /dev/null +++ b/tests/ui/manual_is_ascii_check.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![allow(unused, dead_code)] +#![warn(clippy::manual_is_ascii_check)] + +fn main() { + assert!('x'.is_ascii_lowercase()); + assert!('X'.is_ascii_uppercase()); + assert!(b'x'.is_ascii_lowercase()); + assert!(b'X'.is_ascii_uppercase()); + + let num = '2'; + assert!(num.is_ascii_digit()); + assert!(b'1'.is_ascii_digit()); + assert!('x'.is_ascii_alphabetic()); + + assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); +} + +fn msrv_1_23() { + #![clippy::msrv = "1.23"] + + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +fn msrv_1_24() { + #![clippy::msrv = "1.24"] + + assert!(b'1'.is_ascii_digit()); + assert!('X'.is_ascii_uppercase()); + assert!('x'.is_ascii_alphabetic()); +} + +fn msrv_1_46() { + #![clippy::msrv = "1.46"] + const FOO: bool = matches!('x', '0'..='9'); +} + +fn msrv_1_47() { + #![clippy::msrv = "1.47"] + const FOO: bool = 'x'.is_ascii_digit(); +} diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs new file mode 100644 index 000000000000..be1331610412 --- /dev/null +++ b/tests/ui/manual_is_ascii_check.rs @@ -0,0 +1,45 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![allow(unused, dead_code)] +#![warn(clippy::manual_is_ascii_check)] + +fn main() { + assert!(matches!('x', 'a'..='z')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!(b'x', b'a'..=b'z')); + assert!(matches!(b'X', b'A'..=b'Z')); + + let num = '2'; + assert!(matches!(num, '0'..='9')); + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + + assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); +} + +fn msrv_1_23() { + #![clippy::msrv = "1.23"] + + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +fn msrv_1_24() { + #![clippy::msrv = "1.24"] + + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +fn msrv_1_46() { + #![clippy::msrv = "1.46"] + const FOO: bool = matches!('x', '0'..='9'); +} + +fn msrv_1_47() { + #![clippy::msrv = "1.47"] + const FOO: bool = matches!('x', '0'..='9'); +} diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr new file mode 100644 index 000000000000..c0a9d4db1a15 --- /dev/null +++ b/tests/ui/manual_is_ascii_check.stderr @@ -0,0 +1,70 @@ +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:8:13 + | +LL | assert!(matches!('x', 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_lowercase()` + | + = note: `-D clippy::manual-is-ascii-check` implied by `-D warnings` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:9:13 + | +LL | assert!(matches!('X', 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:10:13 + | +LL | assert!(matches!(b'x', b'a'..=b'z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'x'.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:11:13 + | +LL | assert!(matches!(b'X', b'A'..=b'Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:14:13 + | +LL | assert!(matches!(num, '0'..='9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:15:13 + | +LL | assert!(matches!(b'1', b'0'..=b'9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:16:13 + | +LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:32:13 + | +LL | assert!(matches!(b'1', b'0'..=b'9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:33:13 + | +LL | assert!(matches!('X', 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:34:13 + | +LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:44:23 + | +LL | const FOO: bool = matches!('x', '0'..='9'); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` + +error: aborting due to 11 previous errors + From b0dcb862c60c87c55f4e34c2ecce8914ffe3c4c7 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 22 Apr 2022 18:49:15 -0400 Subject: [PATCH 145/524] Move `needless_collect` into the `methods` lint pass --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/loops/mod.rs | 26 ------------------- clippy_lints/src/methods/mod.rs | 26 +++++++++++++++++++ .../{loops => methods}/needless_collect.rs | 0 4 files changed, 27 insertions(+), 27 deletions(-) rename clippy_lints/src/{loops => methods}/needless_collect.rs (100%) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 747636b7ec39..e9094ca3a093 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -237,7 +237,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::loops::MANUAL_MEMCPY_INFO, crate::loops::MISSING_SPIN_LOOP_INFO, crate::loops::MUT_RANGE_BOUND_INFO, - crate::loops::NEEDLESS_COLLECT_INFO, crate::loops::NEEDLESS_RANGE_LOOP_INFO, crate::loops::NEVER_LOOP_INFO, crate::loops::SAME_ITEM_PUSH_INFO, @@ -346,6 +345,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::MAP_UNWRAP_OR_INFO, crate::methods::MUT_MUTEX_LOCK_INFO, crate::methods::NAIVE_BYTECOUNT_INFO, + crate::methods::NEEDLESS_COLLECT_INFO, crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO, crate::methods::NEEDLESS_OPTION_TAKE_INFO, crate::methods::NEEDLESS_SPLITN_INFO, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 821fe1730239..8e52cac4323c 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -9,7 +9,6 @@ mod manual_flatten; mod manual_memcpy; mod missing_spin_loop; mod mut_range_bound; -mod needless_collect; mod needless_range_loop; mod never_loop; mod same_item_push; @@ -205,28 +204,6 @@ declare_clippy_lint! { "`loop { if let { ... } else break }`, which can be written as a `while let` loop" } -declare_clippy_lint! { - /// ### What it does - /// Checks for functions collecting an iterator when collect - /// is not needed. - /// - /// ### Why is this bad? - /// `collect` causes the allocation of a new data structure, - /// when this allocation may not be needed. - /// - /// ### Example - /// ```rust - /// # let iterator = vec![1].into_iter(); - /// let len = iterator.clone().collect::>().len(); - /// // should be - /// let len = iterator.count(); - /// ``` - #[clippy::version = "1.30.0"] - pub NEEDLESS_COLLECT, - nursery, - "collecting an iterator when collect is not needed" -} - declare_clippy_lint! { /// ### What it does /// Checks `for` loops over slices with an explicit counter @@ -605,7 +582,6 @@ declare_lint_pass!(Loops => [ EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, WHILE_LET_LOOP, - NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, EMPTY_LOOP, WHILE_LET_ON_ITERATOR, @@ -667,8 +643,6 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_immutable_condition::check(cx, condition, body); missing_spin_loop::check(cx, condition, body); } - - needless_collect::check(expr, cx); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 26834dc4fcc6..c413d9baf172 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -54,6 +54,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod mut_mutex_lock; +mod needless_collect; mod needless_option_as_deref; mod needless_option_take; mod no_effect_replace; @@ -3141,6 +3142,28 @@ declare_clippy_lint! { "jumping to the start of stream using `seek` method" } +declare_clippy_lint! { + /// ### What it does + /// Checks for functions collecting an iterator when collect + /// is not needed. + /// + /// ### Why is this bad? + /// `collect` causes the allocation of a new data structure, + /// when this allocation may not be needed. + /// + /// ### Example + /// ```rust + /// # let iterator = vec![1].into_iter(); + /// let len = iterator.clone().collect::>().len(); + /// // should be + /// let len = iterator.count(); + /// ``` + #[clippy::version = "1.30.0"] + pub NEEDLESS_COLLECT, + nursery, + "collecting an iterator when collect is not needed" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -3267,6 +3290,7 @@ impl_lint_pass!(Methods => [ ITER_KV_MAP, SEEK_FROM_CURRENT, SEEK_TO_START_INSTEAD_OF_REWIND, + NEEDLESS_COLLECT, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3317,6 +3341,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, _ => (), } + + needless_collect::check(expr, cx); } #[allow(clippy::too_many_lines)] diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs similarity index 100% rename from clippy_lints/src/loops/needless_collect.rs rename to clippy_lints/src/methods/needless_collect.rs From 586bd3f735a7ec8c0ee03ab3515dfd8a89a31ff4 Mon Sep 17 00:00:00 2001 From: kraktus Date: Sun, 23 Oct 2022 14:00:44 +0200 Subject: [PATCH 146/524] refactor to avoid potential ICE --- clippy_lints/src/excessive_bools.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 453471c8cdda..e1911449242d 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -141,14 +141,13 @@ impl EarlyLintPass for ExcessiveBools { return; } - let struct_bools = variant_data + if let Ok(struct_bools) = variant_data .fields() .iter() .filter(|field| is_bool_ty(&field.ty)) .count() - .try_into() - .unwrap(); - if self.max_struct_bools < struct_bools { + .try_into() && self.max_struct_bools < struct_bools + { span_lint_and_help( cx, STRUCT_EXCESSIVE_BOOLS, @@ -156,8 +155,8 @@ impl EarlyLintPass for ExcessiveBools { &format!("more than {} bools in a struct", self.max_struct_bools), None, "consider using a state machine or refactoring bools into two-variant enums", - ); - } + ) + } }, ItemKind::Impl(box Impl { of_trait: None, items, .. From 9c69e93595e581a92c8385b633e687393cb07471 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 28 Oct 2022 14:54:59 +0200 Subject: [PATCH 147/524] Rewrite `ExcessiveBools` to be a `LateLintPass` lint changelog: [`fn_params_excessive_bools`] Make it possible to allow the lint at the method level --- clippy_lints/src/excessive_bools.rs | 126 +++++++++++----------- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/methods/mod.rs | 13 +-- clippy_utils/src/hir_utils.rs | 10 +- clippy_utils/src/lib.rs | 2 +- tests/ui/fn_params_excessive_bools.rs | 1 + tests/ui/fn_params_excessive_bools.stderr | 20 ++-- 7 files changed, 90 insertions(+), 84 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index e1911449242d..08164955c6a3 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,8 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use clippy_utils::{get_parent_node, is_bool}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Node, Ty}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; +use rustc_target::spec::abi::Abi; declare_clippy_lint! { /// ### What it does @@ -83,6 +86,12 @@ pub struct ExcessiveBools { max_fn_params_bools: u64, } +#[derive(Eq, PartialEq, Debug)] +enum Kind { + Struct, + Fn, +} + impl ExcessiveBools { #[must_use] pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self { @@ -92,21 +101,20 @@ impl ExcessiveBools { } } - fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) { - match fn_sig.header.ext { - Extern::Implicit(_) | Extern::Explicit(_, _) => return, - Extern::None => (), + fn too_many_bools<'tcx>(&self, tys: impl Iterator>, kind: Kind) -> bool { + if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() { + (if Kind::Fn == kind { + self.max_fn_params_bools + } else { + self.max_struct_bools + }) < bools + } else { + false } + } - let fn_sig_bools = fn_sig - .decl - .inputs - .iter() - .filter(|param| is_bool_ty(¶m.ty)) - .count() - .try_into() - .unwrap(); - if self.max_fn_params_bools < fn_sig_bools { + fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) { + if self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { span_lint_and_help( cx, FN_PARAMS_EXCESSIVE_BOOLS, @@ -121,55 +129,53 @@ impl ExcessiveBools { impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); -fn is_bool_ty(ty: &Ty) -> bool { - if let TyKind::Path(None, path) = &ty.kind { - if let [name] = path.segments.as_slice() { - return name.ident.name == sym::bool; - } - } - false -} - -impl EarlyLintPass for ExcessiveBools { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { +impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if item.span.from_expansion() { return; } - match &item.kind { - ItemKind::Struct(variant_data, _) => { - if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) { - return; - } + if let ItemKind::Struct(variant_data, _) = &item.kind { + if cx + .tcx + .hir() + .attrs(item.hir_id()) + .iter() + .any(|attr| attr.has_name(sym::repr)) + { + return; + } + + if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) { + span_lint_and_help( + cx, + STRUCT_EXCESSIVE_BOOLS, + item.span, + &format!("more than {} bools in a struct", self.max_struct_bools), + None, + "consider using a state machine or refactoring bools into two-variant enums", + ) + } + } + } - if let Ok(struct_bools) = variant_data - .fields() - .iter() - .filter(|field| is_bool_ty(&field.ty)) - .count() - .try_into() && self.max_struct_bools < struct_bools - { - span_lint_and_help( - cx, - STRUCT_EXCESSIVE_BOOLS, - item.span, - &format!("more than {} bools in a struct", self.max_struct_bools), - None, - "consider using a state machine or refactoring bools into two-variant enums", - ) - } - }, - ItemKind::Impl(box Impl { - of_trait: None, items, .. - }) - | ItemKind::Trait(box Trait { items, .. }) => { - for item in items { - if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { - self.check_fn_sig(cx, sig, item.span); - } - } - }, - ItemKind::Fn(box Fn { sig, .. }) => self.check_fn_sig(cx, sig, item.span), - _ => (), + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + _: &'tcx Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let Some(fn_header) = fn_kind.header() + && fn_header.abi == Abi::Rust + && if let Some(Node::Item(item)) = get_parent_node(cx.tcx, hir_id) { + !matches!(item.kind, ItemKind::ExternCrate(..)) + } else { + true + } + && !span.from_expansion() { + self.check_fn_sig(cx, fn_decl, span) } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 01da11958e2a..e96075a2673f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -793,7 +793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports)); let max_fn_params_bools = conf.max_fn_params_bools; let max_struct_bools = conf.max_struct_bools; - store.register_early_pass(move || { + store.register_late_pass(move |_| { Box::new(excessive_bools::ExcessiveBools::new( max_struct_bools, max_fn_params_bools, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6665329d1148..996d4cb41234 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -104,11 +104,10 @@ use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; +use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -3966,14 +3965,6 @@ impl OutType { } } -fn is_bool(ty: &hir::Ty<'_>) -> bool { - if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - matches!(path.res, Res::PrimTy(PrimTy::Bool)) - } else { - false - } -} - fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { expected.constness == actual.constness && expected.unsafety == actual.unsafety diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 02b973e5b278..da8c976c952c 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -8,7 +8,7 @@ use rustc_hir::HirIdMap; use rustc_hir::{ ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, - PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, + PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; @@ -1030,6 +1030,14 @@ pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 { h.finish() } +pub fn is_bool(ty: &Ty<'_>) -> bool { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + matches!(path.res, Res::PrimTy(PrimTy::Bool)) + } else { + false + } +} + pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 { let mut h = SpanlessHash::new(cx); h.hash_expr(e); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index dda4660fb7a3..0000896fdb65 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -66,7 +66,7 @@ pub mod visitors; pub use self::attrs::*; pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match}; pub use self::hir_utils::{ - both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash, + both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash, }; use core::ops::ControlFlow; diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index f805bcc9ba8a..4fcd4d54ce62 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -2,6 +2,7 @@ #![allow(clippy::too_many_arguments)] extern "C" { + // Should not lint, most of the time users have no control over extern function signatures fn f(_: bool, _: bool, _: bool, _: bool); } diff --git a/tests/ui/fn_params_excessive_bools.stderr b/tests/ui/fn_params_excessive_bools.stderr index 11627105691b..9d22d3851d2f 100644 --- a/tests/ui/fn_params_excessive_bools.stderr +++ b/tests/ui/fn_params_excessive_bools.stderr @@ -1,5 +1,5 @@ error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:18:1 + --> $DIR/fn_params_excessive_bools.rs:19:1 | LL | fn g(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn g(_: bool, _: bool, _: bool, _: bool) {} = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings` error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:21:1 + --> $DIR/fn_params_excessive_bools.rs:22:1 | LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,23 +16,23 @@ LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:25:5 + --> $DIR/fn_params_excessive_bools.rs:31:5 | -LL | fn f(_: bool, _: bool, _: bool, _: bool); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:30:5 + --> $DIR/fn_params_excessive_bools.rs:38:5 | -LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn f(_: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:42:5 + --> $DIR/fn_params_excessive_bools.rs:43:5 | LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} @@ -42,7 +42,7 @@ LL | | } = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:43:9 + --> $DIR/fn_params_excessive_bools.rs:44:9 | LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From e8c2c3d1c6ac27d0f1fa9125b79f1f927a59cfcf Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 28 Oct 2022 14:57:51 +0200 Subject: [PATCH 148/524] refactor `has_repr_attr` --- clippy_lints/src/excessive_bools.rs | 21 +++++---------------- clippy_lints/src/trailing_empty_array.rs | 8 ++------ clippy_utils/src/lib.rs | 4 ++++ 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 08164955c6a3..08fcf304958f 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{get_parent_node, is_bool}; +use clippy_utils::{has_repr_attr, is_bool}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Node, Ty}; +use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, Span}; +use rustc_span::Span; use rustc_target::spec::abi::Abi; declare_clippy_lint! { @@ -135,13 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { return; } if let ItemKind::Struct(variant_data, _) = &item.kind { - if cx - .tcx - .hir() - .attrs(item.hir_id()) - .iter() - .any(|attr| attr.has_name(sym::repr)) - { + if has_repr_attr(cx, item.hir_id()) { return; } @@ -165,15 +159,10 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { fn_decl: &'tcx FnDecl<'tcx>, _: &'tcx Body<'tcx>, span: Span, - hir_id: HirId, + _: HirId, ) { if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust - && if let Some(Node::Item(item)) = get_parent_node(cx.tcx, hir_id) { - !matches!(item.kind, ItemKind::ExternCrate(..)) - } else { - true - } && !span.from_expansion() { self.check_fn_sig(cx, fn_decl, span) } diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index 58cc057a39ed..712a7a6601ac 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{HirId, Item, ItemKind}; +use clippy_utils::has_repr_attr; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Const; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -72,7 +72,3 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_ } } } - -fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr)) -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0000896fdb65..7ce8bbc1b7b3 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1780,6 +1780,10 @@ pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool { attrs.iter().any(|attr| attr.has_name(symbol)) } +pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { + has_attr(cx.tcx.hir().attrs(hir_id), sym::repr) +} + pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool { let map = &tcx.hir(); let mut prev_enclosing_node = None; From d9b940e2c36eee3172c0f41f570de9e2d8ed95fd Mon Sep 17 00:00:00 2001 From: kraktus Date: Sun, 23 Oct 2022 16:03:05 +0200 Subject: [PATCH 149/524] dogfood --- clippy_lints/src/excessive_bools.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 08fcf304958f..281d0631d06a 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -86,7 +86,7 @@ pub struct ExcessiveBools { max_fn_params_bools: u64, } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, Copy, Clone)] enum Kind { Struct, Fn, @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { &format!("more than {} bools in a struct", self.max_struct_bools), None, "consider using a state machine or refactoring bools into two-variant enums", - ) + ); } } } @@ -164,7 +164,7 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust && !span.from_expansion() { - self.check_fn_sig(cx, fn_decl, span) + self.check_fn_sig(cx, fn_decl, span); } } } From 9e1c7febe261b834d2c787aca2fcd9de577d76d8 Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 29 Oct 2022 16:12:41 +0200 Subject: [PATCH 150/524] `excessive_bools`, do not lint in trait impls Should not lint because the trait might not be changeable by the user We only lint in the trait definition --- clippy_lints/src/excessive_bools.rs | 11 ++++++++--- tests/ui/fn_params_excessive_bools.rs | 2 ++ tests/ui/fn_params_excessive_bools.stderr | 14 +++----------- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 281d0631d06a..68c5e5ab1f1e 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{has_repr_attr, is_bool}; +use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool}; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; @@ -159,11 +159,16 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { fn_decl: &'tcx FnDecl<'tcx>, _: &'tcx Body<'tcx>, span: Span, - _: HirId, + hir_id: HirId, ) { if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust - && !span.from_expansion() { + && !span.from_expansion() + && get_parent_as_impl(cx.tcx, hir_id) + .map_or(true, + |impl_item| impl_item.of_trait.is_none() + ) + { self.check_fn_sig(cx, fn_decl, span); } } diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index 4fcd4d54ce62..2635e433a319 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -35,6 +35,8 @@ impl S { } impl Trait for S { + // Should not lint because the trait might not be changeable by the user + // We only lint in the trait definition fn f(_: bool, _: bool, _: bool, _: bool) {} fn g(_: bool, _: bool, _: bool, _: Vec) {} } diff --git a/tests/ui/fn_params_excessive_bools.stderr b/tests/ui/fn_params_excessive_bools.stderr index 9d22d3851d2f..7edfd07b9db3 100644 --- a/tests/ui/fn_params_excessive_bools.stderr +++ b/tests/ui/fn_params_excessive_bools.stderr @@ -24,15 +24,7 @@ LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:38:5 - | -LL | fn f(_: bool, _: bool, _: bool, _: bool) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider refactoring bools into two-variant enums - -error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:43:5 + --> $DIR/fn_params_excessive_bools.rs:45:5 | LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} @@ -42,12 +34,12 @@ LL | | } = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:44:9 + --> $DIR/fn_params_excessive_bools.rs:46:9 | LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider refactoring bools into two-variant enums -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors From 13b737d5b89c374d7d0c9b2f3968654b9c5ba370 Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 29 Oct 2022 17:43:43 +0200 Subject: [PATCH 151/524] [`fn_params_excessive_bools`] whitelist in signle function in test --- tests/ui/fn_params_excessive_bools.rs | 3 +++ tests/ui/fn_params_excessive_bools.stderr | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index 2635e433a319..7995912110d6 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -25,6 +25,8 @@ struct S; trait Trait { fn f(_: bool, _: bool, _: bool, _: bool); fn g(_: bool, _: bool, _: bool, _: Vec); + #[allow(clippy::fn_params_excessive_bools)] + fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool); } impl S { @@ -39,6 +41,7 @@ impl Trait for S { // We only lint in the trait definition fn f(_: bool, _: bool, _: bool, _: bool) {} fn g(_: bool, _: bool, _: bool, _: Vec) {} + fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool) {} } fn main() { diff --git a/tests/ui/fn_params_excessive_bools.stderr b/tests/ui/fn_params_excessive_bools.stderr index 7edfd07b9db3..8c2383873fe9 100644 --- a/tests/ui/fn_params_excessive_bools.stderr +++ b/tests/ui/fn_params_excessive_bools.stderr @@ -16,7 +16,7 @@ LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:31:5 + --> $DIR/fn_params_excessive_bools.rs:33:5 | LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:45:5 + --> $DIR/fn_params_excessive_bools.rs:48:5 | LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} @@ -34,7 +34,7 @@ LL | | } = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:46:9 + --> $DIR/fn_params_excessive_bools.rs:49:9 | LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 8bfc8bc5e0210dabd2976edad5bf285a96dcb360 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 20 Apr 2022 16:10:18 -0400 Subject: [PATCH 152/524] Lint `needless_collect` on non-std collection types --- .../src/methods/collapsible_str_replace.rs | 6 +- clippy_lints/src/methods/manual_str_repeat.rs | 2 - .../src/methods/map_collect_result_unit.rs | 15 +- clippy_lints/src/methods/mod.rs | 95 ++++---- clippy_lints/src/methods/needless_collect.rs | 223 +++++++++++------- clippy_utils/src/ty.rs | 134 ++++++++++- tests/ui/needless_collect.fixed | 29 +++ tests/ui/needless_collect.rs | 29 +++ tests/ui/needless_collect.stderr | 26 +- 9 files changed, 408 insertions(+), 151 deletions(-) diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs index 501646863fe1..ac61b4377885 100644 --- a/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/clippy_lints/src/methods/collapsible_str_replace.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( // If the parent node's `to` argument is the same as the `to` argument // of the last replace call in the current chain, don't lint as it was already linted if let Some(parent) = get_parent_expr(cx, expr) - && let Some(("replace", _, [current_from, current_to], _)) = method_call(parent) + && let Some(("replace", _, [current_from, current_to], _, _)) = method_call(parent) && eq_expr_value(cx, to, current_to) && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() { @@ -48,7 +48,7 @@ fn collect_replace_calls<'tcx>( let mut from_args = VecDeque::new(); let _: Option<()> = for_each_expr(expr, |e| { - if let Some(("replace", _, [from, to], _)) = method_call(e) { + if let Some(("replace", _, [from, to], _, _)) = method_call(e) { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); from_args.push_front(from); @@ -78,7 +78,7 @@ fn check_consecutive_replace_calls<'tcx>( .collect(); let app = Applicability::MachineApplicable; let earliest_replace_call = replace_methods.methods.front().unwrap(); - if let Some((_, _, [..], span_lo)) = method_call(earliest_replace_call) { + if let Some((_, _, [..], span_lo, _)) = method_call(earliest_replace_call) { span_lint_and_sugg( cx, COLLAPSIBLE_STR_REPLACE, diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 8b6b8f1bf16c..8b798fdb12fc 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -59,10 +59,8 @@ pub(super) fn check( if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String); - if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id); if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id); if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id); if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg); let ctxt = collect_expr.span.ctxt(); diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index d420f144eea1..a0300d278709 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; @@ -11,18 +10,10 @@ use rustc_span::symbol::sym; use super::MAP_COLLECT_RESULT_UNIT; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - iter: &hir::Expr<'_>, - map_fn: &hir::Expr<'_>, - collect_recv: &hir::Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { + // return of collect `Result<(),_>` + let collect_ret_ty = cx.typeck_results().expr_ty(expr); if_chain! { - // called on Iterator - if is_trait_method(cx, collect_recv, sym::Iterator); - // return of collect `Result<(),_>` - let collect_ret_ty = cx.typeck_results().expr_ty(expr); if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result); if let ty::Adt(_, substs) = collect_ret_ty.kind(); if let Some(result_t) = substs.types().next(); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c413d9baf172..1c0bbe086f7c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3296,11 +3296,11 @@ impl_lint_pass!(Methods => [ /// Extracts a method call name, args, and `Span` of the method name. fn method_call<'tcx>( recv: &'tcx hir::Expr<'tcx>, -) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span)> { - if let ExprKind::MethodCall(path, receiver, args, _) = recv.kind { +) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span, Span)> { + if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind { if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { let name = path.ident.name.as_str(); - return Some((name, receiver, args, path.ident.span)); + return Some((name, receiver, args, path.ident.span, call_span)); } } None @@ -3341,8 +3341,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }, _ => (), } - - needless_collect::check(expr, cx); } #[allow(clippy::too_many_lines)] @@ -3488,7 +3486,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { impl Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((name, recv, args, span)) = method_call(expr) { + if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); @@ -3507,28 +3505,31 @@ impl Methods { ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv), - ("collect", []) => match method_call(recv) { - Some((name @ ("cloned" | "copied"), recv2, [], _)) => { - iter_cloned_collect::check(cx, name, expr, recv2); - }, - Some(("map", m_recv, [m_arg], _)) => { - map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); - }, - Some(("take", take_self_arg, [take_arg], _)) => { - if meets_msrv(self.msrv, msrvs::STR_REPEAT) { - manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); - } - }, - _ => {}, + ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { + needless_collect::check(cx, span, expr, recv, call_span); + match method_call(recv) { + Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => { + iter_cloned_collect::check(cx, name, expr, recv2); + }, + Some(("map", m_recv, [m_arg], _, _)) => { + map_collect_result_unit::check(cx, expr, m_recv, m_arg); + }, + Some(("take", take_self_arg, [take_arg], _, _)) => { + if meets_msrv(self.msrv, msrvs::STR_REPEAT) { + manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + } + }, + _ => {}, + } }, ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _)) => { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), + Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => { iter_count::check(cx, expr, recv2, name2); }, - Some(("map", _, [arg], _)) => suspicious_map::check(cx, expr, recv, arg), - Some(("filter", recv2, [arg], _)) => bytecount::check(cx, expr, recv2, arg), - Some(("bytes", recv2, [], _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + Some(("map", _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some(("filter", recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), _ => {}, }, ("drain", [arg]) => { @@ -3540,8 +3541,8 @@ impl Methods { } }, ("expect", [_]) => match method_call(recv) { - Some(("ok", recv, [], _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), + Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), + Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), }, ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests), @@ -3561,13 +3562,13 @@ impl Methods { flat_map_option::check(cx, expr, arg, span); }, ("flatten", []) => match method_call(recv) { - Some(("map", recv, [map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), + Some(("map", recv, [map_arg], map_span, _)) => map_flatten::check(cx, expr, recv, map_arg, map_span), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), _ => {}, }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("for_each", [_]) => { - if let Some(("inspect", _, [_], span2)) = method_call(recv) { + if let Some(("inspect", _, [_], span2, _)) = method_call(recv) { inspect_for_each::check(cx, expr, span2); } }, @@ -3587,12 +3588,12 @@ impl Methods { iter_on_single_or_empty_collections::check(cx, expr, name, recv); }, ("join", [join_arg]) => { - if let Some(("collect", _, _, span)) = method_call(recv) { + if let Some(("collect", _, _, span, _)) = method_call(recv) { unnecessary_join::check(cx, expr, recv, join_arg, span); } }, ("last", []) | ("skip", [_]) => { - if let Some((name2, recv2, args2, _span2)) = method_call(recv) { + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } @@ -3604,13 +3605,13 @@ impl Methods { (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { map_clone::check(cx, expr, recv, m_arg, self.msrv); - if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _)) = method_call(recv) { + if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) { iter_kv_map::check(cx, map_name, expr, recv2, m_arg); } } else { map_err_ignore::check(cx, expr, m_arg); } - if let Some((name, recv2, args, span2)) = method_call(recv) { + if let Some((name, recv2, args, span2,_)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), @@ -3630,7 +3631,7 @@ impl Methods { manual_ok_or::check(cx, expr, recv, def, map); }, ("next", []) => { - if let Some((name2, recv2, args2, _)) = method_call(recv) { + if let Some((name2, recv2, args2, _, _)) = method_call(recv) { match (name2, args2) { ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), @@ -3643,10 +3644,10 @@ impl Methods { } }, ("nth", [n_arg]) => match method_call(recv) { - Some(("bytes", recv2, [], _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), - Some(("iter", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), - Some(("iter_mut", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), + Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), + Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), @@ -3711,7 +3712,7 @@ impl Methods { }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("take", [_arg]) => { - if let Some((name2, recv2, args2, _span2)) = method_call(recv) { + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } @@ -3734,13 +3735,13 @@ impl Methods { }, ("unwrap", []) => { match method_call(recv) { - Some(("get", recv, [get_arg], _)) => { + Some(("get", recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, false); }, - Some(("get_mut", recv, [get_arg], _)) => { + Some(("get_mut", recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, true); }, - Some(("or", recv, [or_arg], or_span)) => { + Some(("or", recv, [or_arg], or_span, _)) => { or_then_unwrap::check(cx, expr, recv, or_arg, or_span); }, _ => {}, @@ -3749,19 +3750,19 @@ impl Methods { }, ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests), ("unwrap_or", [u_arg]) => match method_call(recv) { - Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _)) => { + Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => { manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, - Some(("map", m_recv, [m_arg], span)) => { + Some(("map", m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); }, - Some(("then_some", t_recv, [t_arg], _)) => { + Some(("then_some", t_recv, [t_arg], _, _)) => { obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg); }, _ => {}, }, ("unwrap_or_else", [u_arg]) => match method_call(recv) { - Some(("map", recv, [map_arg], _)) + Some(("map", recv, [map_arg], _, _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); @@ -3782,7 +3783,7 @@ impl Methods { } fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { - if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span)) = method_call(recv) { + if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span, _)) = method_call(recv) { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); } } diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 66f9e28596e8..b088e642e0e9 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -3,94 +3,99 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind}; -use if_chain::if_chain; +use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection}; +use clippy_utils::{ + can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id, + CaptureKind, +}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind}; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind, +}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; -use rustc_span::Span; +use rustc_middle::ty::{self, AssocKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, Span, Symbol}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; -pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - check_needless_collect_direct_usage(expr, cx); - check_needless_collect_indirect_usage(expr, cx); -} -fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if_chain! { - if let ExprKind::MethodCall(method, receiver, args, _) = expr.kind; - if let ExprKind::MethodCall(chain_method, ..) = receiver.kind; - if chain_method.ident.name == sym!(collect) && is_trait_method(cx, receiver, sym::Iterator); - then { - let ty = cx.typeck_results().expr_ty(receiver); - let mut applicability = Applicability::MaybeIncorrect; - let is_empty_sugg = "next().is_none()".to_string(); - let method_name = method.ident.name.as_str(); - let sugg = if is_type_diagnostic_item(cx, ty, sym::Vec) || - is_type_diagnostic_item(cx, ty, sym::VecDeque) || - is_type_diagnostic_item(cx, ty, sym::LinkedList) || - is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { - match method_name { - "len" => "count()".to_string(), - "is_empty" => is_empty_sugg, - "contains" => { - let contains_arg = snippet_with_applicability(cx, args[0].span, "??", &mut applicability); - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - format!("any(|{arg}| x == {pred})") - } - _ => return, - } - } - else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) || - is_type_diagnostic_item(cx, ty, sym::HashMap) { - match method_name { - "is_empty" => is_empty_sugg, - _ => return, - } - } - else { - return; - }; - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - chain_method.ident.span.with_hi(expr.span.hi()), - NEEDLESS_COLLECT_MSG, - "replace with", - sugg, - applicability, - ); - } - } -} +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + name_span: Span, + collect_expr: &'tcx Expr<'_>, + iter_expr: &'tcx Expr<'tcx>, + call_span: Span, +) { + if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) { + match parent { + Node::Expr(parent) => { + if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind { + let mut app = Applicability::MachineApplicable; + let name = name.ident.as_str(); + let collect_ty = cx.typeck_results().expr_ty(collect_expr); -fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if let ExprKind::Block(block, _) = expr.kind { - for stmt in block.stmts { - if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(_, id, ..) = local.pat.kind; - if let Some(init_expr) = local.init; - if let ExprKind::MethodCall(method_name, iter_source, [], ..) = init_expr.kind; - if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); - let ty = cx.typeck_results().expr_ty(init_expr); - if is_type_diagnostic_item(cx, ty, sym::Vec) || - is_type_diagnostic_item(cx, ty, sym::VecDeque) || - is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || - is_type_diagnostic_item(cx, ty, sym::LinkedList); - let iter_ty = cx.typeck_results().expr_ty(iter_source); - if let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)); - if let [iter_call] = &*iter_calls; - then { + let sugg: String = match name { + "len" => { + if let Some(adt) = collect_ty.ty_adt_def() + && matches!( + cx.tcx.get_diagnostic_name(adt.did()), + Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap) + ) + { + "count()".into() + } else { + return; + } + }, + "is_empty" + if is_is_empty_sig(cx, parent.hir_id) + && iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) => + { + "next().is_none()".into() + }, + "contains" => { + if is_contains_sig(cx, parent.hir_id, iter_expr) + && let Some(arg) = args.first() + { + let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind { + (arg.span, "") + } else { + (arg.span, "*") + }; + let snip = snippet_with_applicability(cx, span, "??", &mut app); + format!("any(|x| x == {prefix}{snip})") + } else { + return; + } + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + call_span.with_hi(parent.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + app, + ); + } + }, + Node::Local(l) => { + if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) + = l.pat.kind + && let ty = cx.typeck_results().expr_ty(collect_expr) + && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList].into_iter() + .any(|item| is_type_diagnostic_item(cx, ty, item)) + && let iter_ty = cx.typeck_results().expr_ty(iter_expr) + && let Some(block) = get_enclosing_block(cx, l.hir_id) + && let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)) + && let [iter_call] = &*iter_calls + { let mut used_count_visitor = UsedCountVisitor { cx, id, @@ -102,20 +107,20 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo } // Suggest replacing iter_call with iter_replacement, and removing stmt - let mut span = MultiSpan::from_span(method_name.ident.span); + let mut span = MultiSpan::from_span(name_span); span.push_span_label(iter_call.span, "the iterator could be used here instead"); span_lint_hir_and_then( cx, super::NEEDLESS_COLLECT, - init_expr.hir_id, + collect_expr.hir_id, span, NEEDLESS_COLLECT_MSG, |diag| { - let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); + let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx)); diag.multipart_suggestion( iter_call.get_suggestion_text(), vec![ - (stmt.span, String::new()), + (l.span, String::new()), (iter_call.span, iter_replacement) ], Applicability::MaybeIncorrect, @@ -123,11 +128,61 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo }, ); } - } + }, + _ => (), } } } +/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool` +fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { + cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| { + let sig = cx.tcx.fn_sig(id).skip_binder(); + sig.inputs().len() == 1 && sig.output().is_bool() + }) +} + +/// Checks if `::Item` is the same as `::Item` +fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool { + let item = Symbol::intern("Item"); + if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator) + && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, item, [iter_ty]) + && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty]) + && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( + cx.param_env, + cx.tcx.mk_projection(into_iter_item_proj.item_def_id, into_iter_item_proj.substs) + ) + { + iter_item_ty == into_iter_item_ty + } else { + false + } +} + +/// Checks if the given method call matches the expected signature of +/// `([&[mut]] self, &::Item) -> bool` +fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -> bool { + let typeck = cx.typeck_results(); + if let Some(id) = typeck.type_dependent_def_id(call_id) + && let sig = cx.tcx.fn_sig(id) + && sig.skip_binder().output().is_bool() + && let [_, search_ty] = *sig.skip_binder().inputs() + && let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind() + && let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(iter_item) = cx.tcx + .associated_items(iter_trait) + .find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait) + && let substs = cx.tcx.mk_substs([GenericArg::from(typeck.expr_ty_adjusted(iter_expr))].into_iter()) + && let proj_ty = cx.tcx.mk_projection(iter_item.def_id, substs) + && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty) + { + item_ty == EarlyBinder(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id)) + } else { + false + } +} + struct IterFunction { func: IterFunctionKind, span: Span, diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 2dd8c826ab38..a022aac4bfee 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,8 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, - Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, ParamEnv, Predicate, + PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; @@ -938,3 +939,132 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { (Err(_), _) => 0, } } + +/// Makes the projection type for the named associated type in the given impl or trait impl. +/// +/// This function is for associated types which are "known" to exist, and as such, will only return +/// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions +/// enabled this will check that the named associated type exists, the correct number of +/// substitutions are given, and that the correct kinds of substitutions are given (lifetime, +/// constant or type). This will not check if type normalization would succeed. +pub fn make_projection<'tcx>( + tcx: TyCtxt<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: impl IntoIterator>>, +) -> Option> { + fn helper<'tcx>( + tcx: TyCtxt<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: SubstsRef<'tcx>, + ) -> Option> { + let Some(assoc_item) = tcx + .associated_items(container_id) + .find_by_name_and_kind(tcx, Ident::with_dummy_span(assoc_ty), AssocKind::Type, container_id) + else { + debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`"); + return None; + }; + #[cfg(debug_assertions)] + { + let generics = tcx.generics_of(assoc_item.def_id); + let generic_count = generics.parent_count + generics.params.len(); + let params = generics + .parent + .map_or([].as_slice(), |id| &*tcx.generics_of(id).params) + .iter() + .chain(&generics.params) + .map(|x| &x.kind); + + debug_assert!( + generic_count == substs.len(), + "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\ + note: the expected parameters are: {:#?}\n\ + the given arguments are: `{:#?}`", + assoc_item.def_id, + substs.len(), + generic_count, + params.map(GenericParamDefKind::descr).collect::>(), + substs, + ); + + if let Some((idx, (param, arg))) = params + .clone() + .zip(substs.iter().map(GenericArg::unpack)) + .enumerate() + .find(|(_, (param, arg))| { + !matches!( + (param, arg), + (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) + | (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) + | (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) + ) + }) + { + debug_assert!( + false, + "mismatched subst type at index {}: expected a {}, found `{:?}`\n\ + note: the expected parameters are {:#?}\n\ + the given arguments are {:#?}", + idx, + param.descr(), + arg, + params.map(GenericParamDefKind::descr).collect::>(), + substs, + ); + } + } + + Some(ProjectionTy { + substs, + item_def_id: assoc_item.def_id, + }) + } + helper( + tcx, + container_id, + assoc_ty, + tcx.mk_substs(substs.into_iter().map(Into::into)), + ) +} + +/// Normalizes the named associated type in the given impl or trait impl. +/// +/// This function is for associated types which are "known" to be valid with the given +/// substitutions, and as such, will only return `None` when debug assertions are disabled in order +/// to prevent ICE's. With debug assertions enabled this will check that that type normalization +/// succeeds as well as everything checked by `make_projection`. +pub fn make_normalized_projection<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: impl IntoIterator>>, +) -> Option> { + fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: ProjectionTy<'tcx>) -> Option> { + #[cfg(debug_assertions)] + if let Some((i, subst)) = ty + .substs + .iter() + .enumerate() + .find(|(_, subst)| subst.has_late_bound_regions()) + { + debug_assert!( + false, + "substs contain late-bound region at index `{i}` which can't be normalized.\n\ + use `TyCtxt::erase_late_bound_regions`\n\ + note: subst is `{subst:#?}`", + ); + return None; + } + match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.item_def_id, ty.substs)) { + Ok(ty) => Some(ty), + Err(e) => { + debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); + None + }, + } + } + helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?) +} diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index 6ecbbcb62495..2659ad384885 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -33,4 +33,33 @@ fn main() { // `BinaryHeap` doesn't have `contains` method sample.iter().count(); sample.iter().next().is_none(); + + // Don't lint string from str + let _ = ["", ""].into_iter().collect::().is_empty(); + + let _ = sample.iter().next().is_none(); + let _ = sample.iter().any(|x| x == &0); + + struct VecWrapper(Vec); + impl core::ops::Deref for VecWrapper { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl IntoIterator for VecWrapper { + type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + impl FromIterator for VecWrapper { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } + } + + let _ = sample.iter().next().is_none(); + let _ = sample.iter().any(|x| x == &0); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 8dc69bcf5b38..535ec82982b1 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -33,4 +33,33 @@ fn main() { // `BinaryHeap` doesn't have `contains` method sample.iter().collect::>().len(); sample.iter().collect::>().is_empty(); + + // Don't lint string from str + let _ = ["", ""].into_iter().collect::().is_empty(); + + let _ = sample.iter().collect::>().is_empty(); + let _ = sample.iter().collect::>().contains(&&0); + + struct VecWrapper(Vec); + impl core::ops::Deref for VecWrapper { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl IntoIterator for VecWrapper { + type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + impl FromIterator for VecWrapper { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } + } + + let _ = sample.iter().collect::>().is_empty(); + let _ = sample.iter().collect::>().contains(&&0); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 039091627a8d..584d2a1d8356 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -66,5 +66,29 @@ error: avoid using `collect()` when not needed LL | sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` -error: aborting due to 11 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:40:27 + | +LL | let _ = sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:41:27 + | +LL | let _ = sample.iter().collect::>().contains(&&0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:63:27 + | +LL | let _ = sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:64:27 + | +LL | let _ = sample.iter().collect::>().contains(&&0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` + +error: aborting due to 15 previous errors From 5b1e445b9abff4f9eec0c5911389913108f7ba93 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 7 Nov 2022 14:08:40 -0500 Subject: [PATCH 153/524] Don't lint `explicit_auto_deref` when the target type is a projection containing a generic argument --- clippy_lints/src/dereference.rs | 1 + tests/ui/explicit_auto_deref.fixed | 11 +++++++++++ tests/ui/explicit_auto_deref.rs | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 86e7bc2e6d19..e2624ee80c0f 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1362,6 +1362,7 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc continue; }, ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), + ty::Projection(_) if ty.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty), ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => { Position::ReborrowStable(precedence).into() }, diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index d1d35e5c0eb4..59ff5e4040a3 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -266,4 +266,15 @@ fn main() { } x }; + + trait WithAssoc { + type Assoc: ?Sized; + } + impl WithAssoc for String { + type Assoc = str; + } + fn takes_assoc(_: &T::Assoc) -> T { + unimplemented!() + } + let _: String = takes_assoc(&*String::new()); } diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index deedafad153b..bcfb60c32788 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -266,4 +266,15 @@ fn main() { } *x }; + + trait WithAssoc { + type Assoc: ?Sized; + } + impl WithAssoc for String { + type Assoc = str; + } + fn takes_assoc(_: &T::Assoc) -> T { + unimplemented!() + } + let _: String = takes_assoc(&*String::new()); } From 3d4b73c26da1630318e76f43a7dedfc4dbc716cd Mon Sep 17 00:00:00 2001 From: kraktus Date: Mon, 7 Nov 2022 21:00:37 +0100 Subject: [PATCH 154/524] [`excessive_bools`] lint trait functions even without bodies --- clippy_lints/src/excessive_bools.rs | 14 ++++++++++--- tests/ui/fn_params_excessive_bools.rs | 2 ++ tests/ui/fn_params_excessive_bools.stderr | 24 +++++++++++++++++++---- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 68c5e5ab1f1e..fc2912f696e0 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, Ty}; +use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -114,7 +114,7 @@ impl ExcessiveBools { } fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) { - if self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { + if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { span_lint_and_help( cx, FN_PARAMS_EXCESSIVE_BOOLS, @@ -152,6 +152,15 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { } } + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) { + // functions with a body are already checked by `check_fn` + if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind + && fn_sig.header.abi == Abi::Rust + { + self.check_fn_sig(cx, fn_sig.decl, fn_sig.span); + } + } + fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -163,7 +172,6 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { ) { if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust - && !span.from_expansion() && get_parent_as_impl(cx.tcx, hir_id) .map_or(true, |impl_item| impl_item.of_trait.is_none() diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index 7995912110d6..f53e531629aa 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -23,10 +23,12 @@ fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} struct S; trait Trait { + // should warn for trait functions with and without body fn f(_: bool, _: bool, _: bool, _: bool); fn g(_: bool, _: bool, _: bool, _: Vec); #[allow(clippy::fn_params_excessive_bools)] fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool); + fn i(_: bool, _: bool, _: bool, _: bool) {} } impl S { diff --git a/tests/ui/fn_params_excessive_bools.stderr b/tests/ui/fn_params_excessive_bools.stderr index 8c2383873fe9..43363b46972c 100644 --- a/tests/ui/fn_params_excessive_bools.stderr +++ b/tests/ui/fn_params_excessive_bools.stderr @@ -16,7 +16,23 @@ LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:33:5 + --> $DIR/fn_params_excessive_bools.rs:27:5 + | +LL | fn f(_: bool, _: bool, _: bool, _: bool); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:31:5 + | +LL | fn i(_: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:35:5 | LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +40,7 @@ LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:48:5 + --> $DIR/fn_params_excessive_bools.rs:50:5 | LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} @@ -34,12 +50,12 @@ LL | | } = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:49:9 + --> $DIR/fn_params_excessive_bools.rs:51:9 | LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider refactoring bools into two-variant enums -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors From 80e5856b02060f1185b5976b515eeada18811756 Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Sun, 16 Oct 2022 11:37:26 -0700 Subject: [PATCH 155/524] `result_large_err` show largest variants in err msg --- clippy_lints/src/functions/result.rs | 68 ++++++++++++++++++++------ clippy_lints/src/large_enum_variant.rs | 60 ++++------------------- clippy_utils/src/ty.rs | 42 ++++++++++++++-- tests/ui/result_large_err.rs | 12 +++++ tests/ui/result_large_err.stderr | 30 +++++++++--- 5 files changed, 138 insertions(+), 74 deletions(-) diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 113c4e9f5091..3e288467ba16 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -2,12 +2,12 @@ use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Adt, Ty}; use rustc_span::{sym, Span}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::trait_ref_of_method; -use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item}; +use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; @@ -84,17 +84,57 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S } fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { - let ty_size = approx_ty_size(cx, err_ty); - if ty_size >= large_err_threshold { - span_lint_and_then( - cx, - RESULT_LARGE_ERR, - hir_ty_span, - "the `Err`-variant returned from this function is very large", - |diag: &mut Diagnostic| { - diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); - diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); - }, - ); + if_chain! { + if let Adt(adt, subst) = err_ty.kind(); + if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local(); + if let Some(hir::Node::Item(item)) = cx + .tcx + .hir() + .find_by_def_id(local_def_id); + if let hir::ItemKind::Enum(ref def, _) = item.kind; + then { + let variants_size = AdtVariantInfo::new(cx, *adt, subst); + if variants_size[0].size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag| { + diag.span_label( + def.variants[variants_size[0].ind].span, + format!("the largest variant contains at least {} bytes", variants_size[0].size), + ); + + for variant in &variants_size[1..] { + if variant.size >= large_err_threshold { + let variant_def = &def.variants[variant.ind]; + diag.span_label( + variant_def.span, + format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size), + ); + } + } + + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + } + ); + } + } + else { + let ty_size = approx_ty_size(cx, err_ty); + if ty_size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag: &mut Diagnostic| { + diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + }, + ); + } + } } } diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 8ed7e4bb196c..fd82d9f80f97 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,12 +1,15 @@ //! lint when there is a large size difference between variants on an enum use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy}; +use clippy_utils::{ + diagnostics::span_lint_and_then, + ty::{approx_ty_size, is_copy, AdtVariantInfo}, +}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty}; +use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -72,49 +75,6 @@ impl LargeEnumVariant { } } -struct FieldInfo { - ind: usize, - size: u64, -} - -struct VariantInfo { - ind: usize, - size: u64, - fields_size: Vec, -} - -fn variants_size<'tcx>( - cx: &LateContext<'tcx>, - adt: AdtDef<'tcx>, - subst: &'tcx List>, -) -> Vec { - let mut variants_size = adt - .variants() - .iter() - .enumerate() - .map(|(i, variant)| { - let mut fields_size = variant - .fields - .iter() - .enumerate() - .map(|(i, f)| FieldInfo { - ind: i, - size: approx_ty_size(cx, f.ty(cx.tcx, subst)), - }) - .collect::>(); - fields_size.sort_by(|a, b| (a.size.cmp(&b.size))); - - VariantInfo { - ind: i, - size: fields_size.iter().map(|info| info.size).sum(), - fields_size, - } - }) - .collect::>(); - variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); - variants_size -} - impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { @@ -130,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { if adt.variants().len() <= 1 { return; } - let variants_size = variants_size(cx, *adt, subst); + let variants_size = AdtVariantInfo::new(cx, *adt, subst); let mut difference = variants_size[0].size - variants_size[1].size; if difference > self.maximum_size_difference_allowed { @@ -173,16 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { .fields_size .iter() .rev() - .map_while(|val| { + .map_while(|&(ind, size)| { if difference > self.maximum_size_difference_allowed { - difference = difference.saturating_sub(val.size); + difference = difference.saturating_sub(size); Some(( - fields[val.ind].ty.span, + fields[ind].ty.span, format!( "Box<{}>", snippet_with_applicability( cx, - fields[val.ind].ty.span, + fields[ind].ty.span, "..", &mut applicability ) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a022aac4bfee..3e9c605e3f95 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,9 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, ParamEnv, Predicate, - PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, List, ParamEnv, + Predicate, PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; @@ -845,6 +845,42 @@ pub fn for_each_top_level_late_bound_region( ty.visit_with(&mut V { index: 0, f }) } +pub struct AdtVariantInfo { + pub ind: usize, + pub size: u64, + + /// (ind, size) + pub fields_size: Vec<(usize, u64)>, +} + +impl AdtVariantInfo { + /// Returns ADT variants ordered by size + pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: &'tcx List>) -> Vec { + let mut variants_size = adt + .variants() + .iter() + .enumerate() + .map(|(i, variant)| { + let mut fields_size = variant + .fields + .iter() + .enumerate() + .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst)))) + .collect::>(); + fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size))); + + Self { + ind: i, + size: fields_size.iter().map(|(_, size)| size).sum(), + fields_size, + } + }) + .collect::>(); + variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); + variants_size + } +} + /// Gets the struct or enum variant from the given `Res` pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> { match res { diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index f7df3b856550..9dd27d6dc01a 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -50,6 +50,18 @@ impl LargeErrorVariants<()> { } } +enum MultipleLargeVariants { + _Biggest([u8; 1024]), + _AlsoBig([u8; 512]), + _Ok(usize), +} + +impl MultipleLargeVariants { + fn large_enum_error() -> Result<(), Self> { + Ok(()) + } +} + trait TraitForcesLargeError { fn large_error() -> Result<(), [u8; 512]> { Ok(()) diff --git a/tests/ui/result_large_err.stderr b/tests/ui/result_large_err.stderr index bea101fe20bf..c386edfd2157 100644 --- a/tests/ui/result_large_err.stderr +++ b/tests/ui/result_large_err.stderr @@ -42,13 +42,29 @@ LL | pub fn param_large_error() -> Result<(), (u128, R, FullyDefinedLargeErro error: the `Err`-variant returned from this function is very large --> $DIR/result_large_err.rs:48:34 | +LL | _Omg([u8; 512]), + | --------------- the largest variant contains at least 512 bytes +... LL | pub fn large_enum_error() -> Result<(), Self> { - | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes + | ^^^^^^^^^^^^^^^^ | = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:54:25 + --> $DIR/result_large_err.rs:60:30 + | +LL | _Biggest([u8; 1024]), + | -------------------- the largest variant contains at least 1024 bytes +LL | _AlsoBig([u8; 512]), + | ------------------- the variant `_AlsoBig` contains at least 512 bytes +... +LL | fn large_enum_error() -> Result<(), Self> { + | ^^^^^^^^^^^^^^^^ + | + = help: try reducing the size of `MultipleLargeVariants`, for example by boxing large elements or replacing it with `Box` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:66:25 | LL | fn large_error() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -56,7 +72,7 @@ LL | fn large_error() -> Result<(), [u8; 512]> { = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:73:29 + --> $DIR/result_large_err.rs:85:29 | LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -64,7 +80,7 @@ LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:82:40 + --> $DIR/result_large_err.rs:94:40 | LL | pub fn param_large_union() -> Result<(), UnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -72,7 +88,7 @@ LL | pub fn param_large_union() -> Result<(), UnionError> { = help: try reducing the size of `UnionError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:91:34 + --> $DIR/result_large_err.rs:103:34 | LL | pub fn array_error_subst() -> Result<(), ArrayError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes @@ -80,12 +96,12 @@ LL | pub fn array_error_subst() -> Result<(), ArrayError> { = help: try reducing the size of `ArrayError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:95:31 + --> $DIR/result_large_err.rs:107:31 | LL | pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes | = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box>` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From 70010850e498de0b9b2a0b1874ab8271bb7da180 Mon Sep 17 00:00:00 2001 From: Grachev Mikhail Date: Tue, 8 Nov 2022 15:33:50 +0300 Subject: [PATCH 156/524] Update lint example for `collapsible_str_replace` --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1c0bbe086f7c..686930a0bbe9 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -159,7 +159,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l"); + /// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l"); /// ``` #[clippy::version = "1.65.0"] pub COLLAPSIBLE_STR_REPLACE, From 43a6d0b39ce5403c12264065a1619b49a1670a94 Mon Sep 17 00:00:00 2001 From: Ryan Scheidter Date: Tue, 8 Nov 2022 07:48:39 -0600 Subject: [PATCH 157/524] Fixed Typo --- clippy_lints/src/methods/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1c0bbe086f7c..0a240bbee6cf 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1731,7 +1731,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). + /// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str). /// /// ### Why is this bad? /// Readability, this can be written more concisely as From 84c3a959a7579d368f235a22f0d626980aa693c2 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 9 Nov 2022 19:23:23 +0800 Subject: [PATCH 158/524] bless clippy --- tests/ui/crashes/ice-6250.stderr | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/ui/crashes/ice-6250.stderr b/tests/ui/crashes/ice-6250.stderr index 878897c410cf..4506d1550bd4 100644 --- a/tests/ui/crashes/ice-6250.stderr +++ b/tests/ui/crashes/ice-6250.stderr @@ -23,6 +23,11 @@ error[E0308]: mismatched types | LL | Some(reference) = cache.data.get(key) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | let Some(reference) = cache.data.get(key) { + | +++ error: aborting due to 3 previous errors From 146bd1e13d910bc90c5316f11ddecd35f2673d65 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 9 Nov 2022 16:54:10 +0100 Subject: [PATCH 159/524] Add `unnecessary_safety_doc` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/doc.rs | 94 +++++++++++---- tests/ui/auxiliary/doc_unsafe_macros.rs | 8 ++ tests/ui/doc_unnecessary_unsafe.rs | 148 ++++++++++++++++++++++++ tests/ui/doc_unnecessary_unsafe.stderr | 51 ++++++++ 6 files changed, 278 insertions(+), 25 deletions(-) create mode 100644 tests/ui/doc_unnecessary_unsafe.rs create mode 100644 tests/ui/doc_unnecessary_unsafe.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bd67617f5bf..f1697312d5c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4449,6 +4449,7 @@ Released 2018-09-13 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings +[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 98265410e849..0ff039a4cdfd 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -127,6 +127,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::doc::MISSING_PANICS_DOC_INFO, crate::doc::MISSING_SAFETY_DOC_INFO, crate::doc::NEEDLESS_DOCTEST_MAIN_INFO, + crate::doc::UNNECESSARY_SAFETY_DOC_INFO, crate::double_parens::DOUBLE_PARENS_INFO, crate::drop_forget_ref::DROP_COPY_INFO, crate::drop_forget_ref::DROP_NON_DROP_INFO, diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index fbc00836b390..2ff58ade31fa 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -221,6 +221,42 @@ declare_clippy_lint! { "possible typo for an intra-doc link" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the doc comments of publicly visible + /// safe functions and traits and warns if there is a `# Safety` section. + /// + /// ### Why is this bad? + /// Safe functions and traits are safe to implement and therefore do not + /// need to describe safety preconditions that users are required to uphold. + /// + /// ### Examples + /// ```rust + ///# type Universe = (); + /// /// # Safety + /// /// + /// /// This function should not be called before the horsemen are ready. + /// pub fn start_apocalypse_but_safely(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + /// + /// The function is safe, so there shouldn't be any preconditions + /// that have to be explained for safety reasons. + /// + /// ```rust + ///# type Universe = (); + /// /// This function should really be documented + /// pub fn start_apocalypse(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub UNNECESSARY_SAFETY_DOC, + style, + "`pub fn` or `pub trait` with `# Safety` docs" +} + #[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { @@ -243,7 +279,8 @@ impl_lint_pass!(DocMarkdown => [ MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, - NEEDLESS_DOCTEST_MAIN + NEEDLESS_DOCTEST_MAIN, + UNNECESSARY_SAFETY_DOC, ]); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { @@ -254,7 +291,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; match item.kind { hir::ItemKind::Fn(ref sig, _, body_id) => { if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { @@ -271,15 +308,20 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { hir::ItemKind::Impl(impl_) => { self.in_trait_impl = impl_.of_trait.is_some(); }, - hir::ItemKind::Trait(_, unsafety, ..) => { - if !headers.safety && unsafety == hir::Unsafety::Unsafe { - span_lint( - cx, - MISSING_SAFETY_DOC, - cx.tcx.def_span(item.def_id), - "docs for unsafe trait missing `# Safety` section", - ); - } + hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) { + (false, hir::Unsafety::Unsafe) => span_lint( + cx, + MISSING_SAFETY_DOC, + cx.tcx.def_span(item.def_id), + "docs for unsafe trait missing `# Safety` section", + ), + (true, hir::Unsafety::Normal) => span_lint( + cx, + UNNECESSARY_SAFETY_DOC, + cx.tcx.def_span(item.def_id), + "docs for safe trait have unnecessary `# Safety` section", + ), + _ => (), }, _ => (), } @@ -293,7 +335,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { if !in_external_macro(cx.tcx.sess, item.span) { lint_for_missing_headers(cx, item.def_id.def_id, sig, headers, None, None); @@ -303,7 +345,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) { return; } @@ -343,14 +385,20 @@ fn lint_for_missing_headers( } let span = cx.tcx.def_span(def_id); - - if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe { - span_lint( + 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( @@ -452,7 +500,7 @@ struct DocHeaders { panics: bool, } -fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[Attribute]) -> DocHeaders { +fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[Attribute]) -> Option { use pulldown_cmark::{BrokenLink, CowStr, Options}; /// 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 are @@ -473,11 +521,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ } else if attr.has_name(sym::doc) { // ignore mix of sugared and non-sugared doc // don't trigger the safety or errors check - return DocHeaders { - safety: true, - errors: true, - panics: true, - }; + return None; } } @@ -489,7 +533,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ } if doc.is_empty() { - return DocHeaders::default(); + return Some(DocHeaders::default()); } let mut cb = fake_broken_link_callback; @@ -512,7 +556,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ (previous, current) => Err(((previous, previous_range), (current, current_range))), } }); - check_doc(cx, valid_idents, events, &spans) + Some(check_doc(cx, valid_idents, events, &spans)) } const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; diff --git a/tests/ui/auxiliary/doc_unsafe_macros.rs b/tests/ui/auxiliary/doc_unsafe_macros.rs index 869672d1eda5..3d917e3dc75e 100644 --- a/tests/ui/auxiliary/doc_unsafe_macros.rs +++ b/tests/ui/auxiliary/doc_unsafe_macros.rs @@ -6,3 +6,11 @@ macro_rules! undocd_unsafe { } }; } +#[macro_export] +macro_rules! undocd_safe { + () => { + pub fn vey_oy() { + unimplemented!(); + } + }; +} diff --git a/tests/ui/doc_unnecessary_unsafe.rs b/tests/ui/doc_unnecessary_unsafe.rs new file mode 100644 index 000000000000..d9e9363b0f4b --- /dev/null +++ b/tests/ui/doc_unnecessary_unsafe.rs @@ -0,0 +1,148 @@ +// aux-build:doc_unsafe_macros.rs + +#![allow(clippy::let_unit_value)] + +#[macro_use] +extern crate doc_unsafe_macros; + +/// This is has no safety section, and does not need one either +pub fn destroy_the_planet() { + unimplemented!(); +} + +/// This one does not need a `Safety` section +/// +/// # Safety +/// +/// This function shouldn't be called unless the horsemen are ready +pub fn apocalypse(universe: &mut ()) { + unimplemented!(); +} + +/// This is a private function, skip to match behavior with `missing_safety_doc`. +/// +/// # Safety +/// +/// Boo! +fn you_dont_see_me() { + unimplemented!(); +} + +mod private_mod { + /// This is public but unexported function, skip to match behavior with `missing_safety_doc`. + /// + /// # Safety + /// + /// Very safe! + pub fn only_crate_wide_accessible() { + unimplemented!(); + } + + /// # Safety + /// + /// Unnecessary safety! + pub fn republished() { + unimplemented!(); + } +} + +pub use private_mod::republished; + +pub trait SafeTraitSafeMethods { + fn woefully_underdocumented(self); + + /// # Safety + /// + /// Unnecessary! + fn documented(self); +} + +pub trait SafeTrait { + fn method(); +} + +/// # Safety +/// +/// Unnecessary! +pub trait DocumentedSafeTrait { + fn method2(); +} + +pub struct Struct; + +impl SafeTraitSafeMethods for Struct { + fn woefully_underdocumented(self) { + // all is well + } + + fn documented(self) { + // all is still well + } +} + +impl SafeTrait for Struct { + fn method() {} +} + +impl DocumentedSafeTrait for Struct { + fn method2() {} +} + +impl Struct { + /// # Safety + /// + /// Unnecessary! + pub fn documented() -> Self { + unimplemented!(); + } + + pub fn undocumented(&self) { + unimplemented!(); + } + + /// Private, fine again to stay consistent with `missing_safety_doc`. + /// + /// # Safety + /// + /// Unnecessary! + fn private(&self) { + unimplemented!(); + } +} + +macro_rules! very_safe { + () => { + pub fn whee() { + unimplemented!() + } + + /// # Safety + /// + /// Driving is very safe already! + pub fn drive() { + whee() + } + }; +} + +very_safe!(); + +// we don't lint code from external macros +undocd_safe!(); + +fn main() {} + +// do not lint if any parent has `#[doc(hidden)]` attribute +// see #7347 +#[doc(hidden)] +pub mod __macro { + pub struct T; + impl T { + pub unsafe fn f() {} + } +} + +/// # Implementation safety +pub trait DocumentedSafeTraitWithImplementationHeader { + fn method(); +} diff --git a/tests/ui/doc_unnecessary_unsafe.stderr b/tests/ui/doc_unnecessary_unsafe.stderr new file mode 100644 index 000000000000..83b2efbb346b --- /dev/null +++ b/tests/ui/doc_unnecessary_unsafe.stderr @@ -0,0 +1,51 @@ +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:18:1 + | +LL | pub fn apocalypse(universe: &mut ()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-safety-doc` implied by `-D warnings` + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:44:5 + | +LL | pub fn republished() { + | ^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:57:5 + | +LL | fn documented(self); + | ^^^^^^^^^^^^^^^^^^^^ + +error: docs for safe trait have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:67:1 + | +LL | pub trait DocumentedSafeTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:95:5 + | +LL | pub fn documented() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:122:9 + | +LL | pub fn drive() { + | ^^^^^^^^^^^^^^ +... +LL | very_safe!(); + | ------------ in this macro invocation + | + = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for safe trait have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:146:1 + | +LL | pub trait DocumentedSafeTraitWithImplementationHeader { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + From 708c2d95c14829e0acbe7f40d9bea39e7bb03450 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Sat, 1 Oct 2022 15:58:10 +0200 Subject: [PATCH 160/524] feat: add and implement unchecked_duration_subtraction lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 + .../src/unchecked_duration_subtraction.rs | 107 ++++++++++++++++++ clippy_lints/src/utils/conf.rs | 2 +- 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/unchecked_duration_subtraction.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bd67617f5bf..eb4ca9801432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4428,6 +4428,7 @@ Released 2018-09-13 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds +[`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a4bacb780349..a5407b65b88d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -279,6 +279,7 @@ mod trailing_empty_array; mod trait_bounds; mod transmute; mod types; +mod unchecked_duration_subtraction; mod undocumented_unsafe_blocks; mod unicode; mod uninit_vec; @@ -921,6 +922,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); + store.register_late_pass(move || Box::new(unchecked_duration_subtraction::UncheckedDurationSubtraction::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unchecked_duration_subtraction.rs b/clippy_lints/src/unchecked_duration_subtraction.rs new file mode 100644 index 000000000000..fb08067de8d4 --- /dev/null +++ b/clippy_lints/src/unchecked_duration_subtraction.rs @@ -0,0 +1,107 @@ +use clippy_utils::{diagnostics, meets_msrv, msrvs, source, ty}; +use rustc_errors::Applicability; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::sym; + +declare_clippy_lint! { + /// ### What it does + /// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. + /// + /// ### Why is this bad? + /// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or + /// unintentional panics. + /// + /// ### Example + /// ```rust + /// let time_passed = Instant::now() - Duration::from_secs(5); + /// ``` + /// + /// Use instead: + /// ```rust + /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); + /// ``` + /// + /// [`Duration`]: std::time::Duration + /// [`Instant::now()`]: std::time::Instant::now; + #[clippy::version = "1.65.0"] + pub UNCHECKED_DURATION_SUBTRACTION, + suspicious, + "finds unchecked subtraction of a 'Duration' from an 'Instant'" +} + +pub struct UncheckedDurationSubtraction { + msrv: Option, +} + +impl UncheckedDurationSubtraction { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(UncheckedDurationSubtraction => [UNCHECKED_DURATION_SUBTRACTION]); + +impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) { + return; + } + + if_chain! { + if let ExprKind::Binary(op, lhs, rhs) = expr.kind; + + if let BinOpKind::Sub = op.node; + + // get types of left and right side + if is_an_instant(cx, lhs); + if is_a_duration(cx, rhs); + + then { + print_lint_and_sugg(cx, lhs, rhs, expr) + } + } + } +} + +fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + + match expr_ty.kind() { + rustc_middle::ty::Adt(def, _) => { + clippy_utils::match_def_path(cx, dbg!(def).did(), &clippy_utils::paths::INSTANT) + }, + _ => false, + } +} + +fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration) +} + +fn print_lint_and_sugg(cx: &LateContext<'_>, left_expr: &Expr<'_>, right_expr: &Expr<'_>, expr: &Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + + let left_expr = + source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability); + let right_expr = source::snippet_with_applicability( + cx, + right_expr.span, + "std::time::Duration::from_secs(1)", + &mut applicability, + ); + + diagnostics::span_lint_and_sugg( + cx, + UNCHECKED_DURATION_SUBTRACTION, + expr.span, + "unchecked subtraction of a 'Duration' from an 'Instant'", + "try", + format!("{}.checked_sub({}).unwrap();", left_expr, right_expr), + applicability, + ); +} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0cf49ca6d429..ab58e9b8b68d 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -213,7 +213,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From 3b4e42b91b0df11c43983c6fe67bb971f472aa3f Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Sat, 1 Oct 2022 16:02:57 +0200 Subject: [PATCH 161/524] test: add tests for 'unchecked_duration_subtraction' lint --- tests/ui/unchecked_duration_subtraction.rs | 16 ++++++++++ .../ui/unchecked_duration_subtraction.stderr | 32 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/ui/unchecked_duration_subtraction.rs create mode 100644 tests/ui/unchecked_duration_subtraction.stderr diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs new file mode 100644 index 000000000000..fff1d13720d9 --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.rs @@ -0,0 +1,16 @@ +#![warn(clippy::unchecked_duration_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first - second; + + let _ = Instant::now() - Duration::from_secs(5); + + let _ = _first - Duration::from_secs(5); + + let _ = Instant::now() - second; +} diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr new file mode 100644 index 000000000000..827b18a5a097 --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -0,0 +1,32 @@ +[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:9:13 + | +LL | let _ = _first - second; + | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap();` + | + = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` + +[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:11:13 + | +LL | let _ = Instant::now() - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();` + +[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:13:13 + | +LL | let _ = _first - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();` + +[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:15:13 + | +LL | let _ = Instant::now() - second; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap();` + +error: aborting due to 4 previous errors + From b280dbe5f73286a3f2a02e93c6c3e42181ec8aca Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Sat, 1 Oct 2022 16:50:45 +0200 Subject: [PATCH 162/524] docs: add docs for `unchecked_duration_subtraction` lint --- src/docs/unchecked_duration_subtraction.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/docs/unchecked_duration_subtraction.txt diff --git a/src/docs/unchecked_duration_subtraction.txt b/src/docs/unchecked_duration_subtraction.txt new file mode 100644 index 000000000000..6b9c9308c87e --- /dev/null +++ b/src/docs/unchecked_duration_subtraction.txt @@ -0,0 +1,19 @@ +### What it does +Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. + +### Why is this bad? +Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or +unintentional panics. + +### Example +``` +let time_passed = Instant::now() - Duration::from_secs(5); +``` + +Use instead: +``` +let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); +``` + +[`Duration`]: std::time::Duration +[`Instant::now()`]: std::time::Instant::now; \ No newline at end of file From b485832b16a388dbcfd3c42030356959e7662a3d Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Sat, 1 Oct 2022 16:50:56 +0200 Subject: [PATCH 163/524] style: remove `dbg!` call --- clippy_lints/src/unchecked_duration_subtraction.rs | 4 +--- tests/ui/unchecked_duration_subtraction.stderr | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/clippy_lints/src/unchecked_duration_subtraction.rs b/clippy_lints/src/unchecked_duration_subtraction.rs index fb08067de8d4..ae7960006629 100644 --- a/clippy_lints/src/unchecked_duration_subtraction.rs +++ b/clippy_lints/src/unchecked_duration_subtraction.rs @@ -71,9 +71,7 @@ fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr); match expr_ty.kind() { - rustc_middle::ty::Adt(def, _) => { - clippy_utils::match_def_path(cx, dbg!(def).did(), &clippy_utils::paths::INSTANT) - }, + rustc_middle::ty::Adt(def, _) => clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT), _ => false, } } diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr index 827b18a5a097..6b297a01c7be 100644 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -1,4 +1,3 @@ -[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:9:13 | @@ -7,21 +6,18 @@ LL | let _ = _first - second; | = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` -[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:11:13 | LL | let _ = Instant::now() - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();` -[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:13:13 | LL | let _ = _first - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();` -[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:15:13 | From 2f2eb2e4baace68868af2f4ac3ec7aed073139ad Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 7 Nov 2022 20:58:49 +0100 Subject: [PATCH 164/524] refactor: lint man. instant elapsed and unch. dur. subtr. in single pass --- ..._subtraction.rs => instant_subtraction.rs} | 78 ++++++++++++++++--- clippy_lints/src/lib.rs | 5 +- clippy_lints/src/manual_instant_elapsed.rs | 69 ---------------- 3 files changed, 71 insertions(+), 81 deletions(-) rename clippy_lints/src/{unchecked_duration_subtraction.rs => instant_subtraction.rs} (52%) delete mode 100644 clippy_lints/src/manual_instant_elapsed.rs diff --git a/clippy_lints/src/unchecked_duration_subtraction.rs b/clippy_lints/src/instant_subtraction.rs similarity index 52% rename from clippy_lints/src/unchecked_duration_subtraction.rs rename to clippy_lints/src/instant_subtraction.rs index ae7960006629..8cc5643c9a7b 100644 --- a/clippy_lints/src/unchecked_duration_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -1,17 +1,48 @@ -use clippy_utils::{diagnostics, meets_msrv, msrvs, source, ty}; +use clippy_utils::{ + diagnostics::{self, span_lint_and_sugg}, + meets_msrv, msrvs, source, ty, +}; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::sym; +use rustc_span::{source_map::Spanned, sym}; + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between `Instant::now()` and another `Instant`. + /// + /// ### Why is this bad? + /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns + /// as `Instant` subtraction saturates. + /// + /// `prev_instant.elapsed()` also more clearly signals intention. + /// + /// ### Example + /// ```rust + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = Instant::now() - prev_instant; + /// ``` + /// Use instead: + /// ```rust + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = prev_instant.elapsed(); + /// ``` + #[clippy::version = "1.64.0"] + pub MANUAL_INSTANT_ELAPSED, + pedantic, + "subtraction between `Instant::now()` and previous `Instant`" +} declare_clippy_lint! { /// ### What it does /// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. /// /// ### Why is this bad? - /// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or + /// Unchecked subtraction could cause underflow on certain platforms, leading to /// unintentional panics. /// /// ### Example @@ -32,21 +63,39 @@ declare_clippy_lint! { "finds unchecked subtraction of a 'Duration' from an 'Instant'" } -pub struct UncheckedDurationSubtraction { +pub struct InstantSubtraction { msrv: Option, } -impl UncheckedDurationSubtraction { +impl InstantSubtraction { #[must_use] pub fn new(msrv: Option) -> Self { Self { msrv } } } -impl_lint_pass!(UncheckedDurationSubtraction => [UNCHECKED_DURATION_SUBTRACTION]); +impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]); + +impl LateLintPass<'_> for InstantSubtraction { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind + && check_instant_now_call(cx, lhs) + && let ty_resolved = cx.typeck_results().expr_ty(rhs) + && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind() + && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT) + && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs) + { + span_lint_and_sugg( + cx, + MANUAL_INSTANT_ELAPSED, + expr.span, + "manual implementation of `Instant::elapsed`", + "try", + format!("{}.elapsed()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); + } -impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) { return; } @@ -67,6 +116,17 @@ impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction { } } +fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { + if let ExprKind::Call(fn_expr, []) = expr_block.kind + && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) + && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) + { + true + } else { + false + } +} + fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a5407b65b88d..ce0e7ebfbf1b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -151,6 +151,7 @@ mod inherent_impl; mod inherent_to_string; mod init_numbered_fields; mod inline_fn_without_body; +mod instant_subtraction; mod int_plus_one; mod invalid_upcast_comparisons; mod invalid_utf8_in_unchecked; @@ -172,7 +173,6 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; -mod manual_instant_elapsed; mod manual_is_ascii_check; mod manual_let_else; mod manual_non_exhaustive; @@ -279,7 +279,6 @@ mod trailing_empty_array; mod trait_bounds; mod transmute; mod types; -mod unchecked_duration_subtraction; mod undocumented_unsafe_blocks; mod unicode; mod uninit_vec; @@ -908,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed)); + store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); diff --git a/clippy_lints/src/manual_instant_elapsed.rs b/clippy_lints/src/manual_instant_elapsed.rs deleted file mode 100644 index 1e60aa02d3ca..000000000000 --- a/clippy_lints/src/manual_instant_elapsed.rs +++ /dev/null @@ -1,69 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; - -declare_clippy_lint! { - /// ### What it does - /// Lints subtraction between `Instant::now()` and another `Instant`. - /// - /// ### Why is this bad? - /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns - /// as `Instant` subtraction saturates. - /// - /// `prev_instant.elapsed()` also more clearly signals intention. - /// - /// ### Example - /// ```rust - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = Instant::now() - prev_instant; - /// ``` - /// Use instead: - /// ```rust - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = prev_instant.elapsed(); - /// ``` - #[clippy::version = "1.65.0"] - pub MANUAL_INSTANT_ELAPSED, - pedantic, - "subtraction between `Instant::now()` and previous `Instant`" -} - -declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]); - -impl LateLintPass<'_> for ManualInstantElapsed { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind - && check_instant_now_call(cx, lhs) - && let ty_resolved = cx.typeck_results().expr_ty(rhs) - && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind() - && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT) - && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs) - { - span_lint_and_sugg( - cx, - MANUAL_INSTANT_ELAPSED, - expr.span, - "manual implementation of `Instant::elapsed`", - "try", - format!("{}.elapsed()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); - } - } -} - -fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { - if let ExprKind::Call(fn_expr, []) = expr_block.kind - && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) - && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) - { - true - } else { - false - } -} From a566eb37659ecbdcfe1403f8ccab753714dbec60 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 7 Nov 2022 20:59:55 +0100 Subject: [PATCH 165/524] docs: update docs for unchecked duration subtr. lint --- src/docs/unchecked_duration_subtraction.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/unchecked_duration_subtraction.txt b/src/docs/unchecked_duration_subtraction.txt index 6b9c9308c87e..15a4c02c6065 100644 --- a/src/docs/unchecked_duration_subtraction.txt +++ b/src/docs/unchecked_duration_subtraction.txt @@ -2,7 +2,7 @@ Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. ### Why is this bad? -Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or +Unchecked subtraction could cause underflow on certain platforms, leading to unintentional panics. ### Example From 80e3553f290cd252646c61086e9461fc7618b694 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 7 Nov 2022 21:34:24 +0100 Subject: [PATCH 166/524] refactor: improve code re-use in InstantSubtraction lint pass --- clippy_lints/src/instant_subtraction.rs | 85 +++++++++++++++---------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 8cc5643c9a7b..3166c62dad43 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -1,6 +1,8 @@ use clippy_utils::{ diagnostics::{self, span_lint_and_sugg}, - meets_msrv, msrvs, source, ty, + meets_msrv, msrvs, source, + sugg::Sugg, + ty, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -78,45 +80,41 @@ impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATIO impl LateLintPass<'_> for InstantSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind - && check_instant_now_call(cx, lhs) - && let ty_resolved = cx.typeck_results().expr_ty(rhs) - && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind() - && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT) - && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs) + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = expr.kind { - span_lint_and_sugg( - cx, - MANUAL_INSTANT_ELAPSED, - expr.span, - "manual implementation of `Instant::elapsed`", - "try", - format!("{}.elapsed()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); - } - - if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) { - return; - } - - if_chain! { - if let ExprKind::Binary(op, lhs, rhs) = expr.kind; - - if let BinOpKind::Sub = op.node; - - // get types of left and right side - if is_an_instant(cx, lhs); - if is_a_duration(cx, rhs); - - then { - print_lint_and_sugg(cx, lhs, rhs, expr) + if_chain! { + if is_instant_now_call(cx, lhs); + + if is_an_instant(cx, rhs); + if let Some(sugg) = Sugg::hir_opt(cx, rhs); + + then { + print_manual_instant_elapsed_sugg(cx, expr, sugg) + } else { + if_chain! { + if !expr.span.from_expansion(); + if meets_msrv(self.msrv, msrvs::TRY_FROM); + + if is_an_instant(cx, lhs); + if is_a_duration(cx, rhs); + + then { + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr) + } + } + } } } } } -fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { +fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, []) = expr_block.kind && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) @@ -141,7 +139,24 @@ fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration) } -fn print_lint_and_sugg(cx: &LateContext<'_>, left_expr: &Expr<'_>, right_expr: &Expr<'_>, expr: &Expr<'_>) { +fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { + span_lint_and_sugg( + cx, + MANUAL_INSTANT_ELAPSED, + expr.span, + "manual implementation of `Instant::elapsed`", + "try", + format!("{}.elapsed()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); +} + +fn print_unchecked_duration_subtraction_sugg( + cx: &LateContext<'_>, + left_expr: &Expr<'_>, + right_expr: &Expr<'_>, + expr: &Expr<'_>, +) { let mut applicability = Applicability::MachineApplicable; let left_expr = From 4bd6d0beb0ef8ad72ad8ce6a28eb25a48f9cbc4b Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 7 Nov 2022 21:37:04 +0100 Subject: [PATCH 167/524] test: update tests for manual_instant_elapsed lint --- tests/ui/manual_instant_elapsed.fixed | 1 + tests/ui/manual_instant_elapsed.rs | 1 + tests/ui/manual_instant_elapsed.stderr | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/ui/manual_instant_elapsed.fixed b/tests/ui/manual_instant_elapsed.fixed index 0fa776b7b2e4..85a91543c893 100644 --- a/tests/ui/manual_instant_elapsed.fixed +++ b/tests/ui/manual_instant_elapsed.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::unchecked_duration_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.rs b/tests/ui/manual_instant_elapsed.rs index 5b11b84535dd..c98cb15b9164 100644 --- a/tests/ui/manual_instant_elapsed.rs +++ b/tests/ui/manual_instant_elapsed.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::unchecked_duration_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.stderr b/tests/ui/manual_instant_elapsed.stderr index 5537f5642a23..4ce1f689107e 100644 --- a/tests/ui/manual_instant_elapsed.stderr +++ b/tests/ui/manual_instant_elapsed.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Instant::elapsed` - --> $DIR/manual_instant_elapsed.rs:17:20 + --> $DIR/manual_instant_elapsed.rs:18:20 | LL | let duration = Instant::now() - prev_instant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `prev_instant.elapsed()` @@ -7,7 +7,7 @@ LL | let duration = Instant::now() - prev_instant; = note: `-D clippy::manual-instant-elapsed` implied by `-D warnings` error: manual implementation of `Instant::elapsed` - --> $DIR/manual_instant_elapsed.rs:26:5 + --> $DIR/manual_instant_elapsed.rs:27:5 | LL | Instant::now() - *ref_to_instant; // to ensure parens are added correctly | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*ref_to_instant).elapsed()` From 852d80245148cdd23c5516bea015b0f3bf277ed4 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 7 Nov 2022 22:21:32 +0100 Subject: [PATCH 168/524] style: inline variables in `format!` --- clippy_lints/src/instant_subtraction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 3166c62dad43..c039f1b5c6a6 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -174,7 +174,7 @@ fn print_unchecked_duration_subtraction_sugg( expr.span, "unchecked subtraction of a 'Duration' from an 'Instant'", "try", - format!("{}.checked_sub({}).unwrap();", left_expr, right_expr), + format!("{left_expr}.checked_sub({right_expr}).unwrap();"), applicability, ); } From 62ab4fb9c265b9e25d61c99511cd26de0260f435 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Tue, 8 Nov 2022 10:22:58 +0100 Subject: [PATCH 169/524] fix: add rust-fix annotation to unckd. duration subtr. lint test --- tests/ui/unchecked_duration_subtraction.fixed | 17 +++++++++++++++++ tests/ui/unchecked_duration_subtraction.rs | 1 + tests/ui/unchecked_duration_subtraction.stderr | 8 ++++---- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 tests/ui/unchecked_duration_subtraction.fixed diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_duration_subtraction.fixed new file mode 100644 index 000000000000..bcc231d93b16 --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.fixed @@ -0,0 +1,17 @@ +// run-rustfix +#![warn(clippy::unchecked_duration_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first.checked_sub(second).unwrap();; + + let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();; + + let _ = _first.checked_sub(Duration::from_secs(5)).unwrap();; + + let _ = Instant::now().checked_sub(second).unwrap();; +} diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs index fff1d13720d9..a14a7ea57cc5 100644 --- a/tests/ui/unchecked_duration_subtraction.rs +++ b/tests/ui/unchecked_duration_subtraction.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::unchecked_duration_subtraction)] use std::time::{Duration, Instant}; diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr index 6b297a01c7be..b949dbf701a0 100644 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -1,5 +1,5 @@ error: unchecked subtraction of a 'Duration' from an 'Instant' - --> $DIR/unchecked_duration_subtraction.rs:9:13 + --> $DIR/unchecked_duration_subtraction.rs:10:13 | LL | let _ = _first - second; | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap();` @@ -7,19 +7,19 @@ LL | let _ = _first - second; = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> $DIR/unchecked_duration_subtraction.rs:11:13 + --> $DIR/unchecked_duration_subtraction.rs:12:13 | LL | let _ = Instant::now() - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> $DIR/unchecked_duration_subtraction.rs:13:13 + --> $DIR/unchecked_duration_subtraction.rs:14:13 | LL | let _ = _first - Duration::from_secs(5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();` error: unchecked subtraction of a 'Duration' from an 'Instant' - --> $DIR/unchecked_duration_subtraction.rs:15:13 + --> $DIR/unchecked_duration_subtraction.rs:16:13 | LL | let _ = Instant::now() - second; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap();` From 862ac29192bd4e2c9db04e9763dfd1a35a00a9cb Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Tue, 8 Nov 2022 15:04:32 +0100 Subject: [PATCH 170/524] fix: remove (redundant) semicolon in lint suggestion --- clippy_lints/src/instant_subtraction.rs | 2 +- tests/ui/unchecked_duration_subtraction.fixed | 8 ++++---- tests/ui/unchecked_duration_subtraction.stderr | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index c039f1b5c6a6..d83beb622cfd 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -174,7 +174,7 @@ fn print_unchecked_duration_subtraction_sugg( expr.span, "unchecked subtraction of a 'Duration' from an 'Instant'", "try", - format!("{left_expr}.checked_sub({right_expr}).unwrap();"), + format!("{left_expr}.checked_sub({right_expr}).unwrap()"), applicability, ); } diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_duration_subtraction.fixed index bcc231d93b16..a0e49a8beb1e 100644 --- a/tests/ui/unchecked_duration_subtraction.fixed +++ b/tests/ui/unchecked_duration_subtraction.fixed @@ -7,11 +7,11 @@ fn main() { let _first = Instant::now(); let second = Duration::from_secs(3); - let _ = _first.checked_sub(second).unwrap();; + let _ = _first.checked_sub(second).unwrap(); - let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();; + let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); - let _ = _first.checked_sub(Duration::from_secs(5)).unwrap();; + let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); - let _ = Instant::now().checked_sub(second).unwrap();; + let _ = Instant::now().checked_sub(second).unwrap(); } diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr index b949dbf701a0..a2e0aa1d7c08 100644 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -2,7 +2,7 @@ error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:10:13 | LL | let _ = _first - second; - | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap();` + | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` | = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` @@ -10,19 +10,19 @@ error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:12:13 | LL | let _ = Instant::now() - Duration::from_secs(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:14:13 | LL | let _ = _first - Duration::from_secs(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` error: unchecked subtraction of a 'Duration' from an 'Instant' --> $DIR/unchecked_duration_subtraction.rs:16:13 | LL | let _ = Instant::now() - second; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap();` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` error: aborting due to 4 previous errors From 36eac0cb4ae2e372193f73c3181d5290431d3ae4 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Thu, 10 Nov 2022 15:55:45 +0100 Subject: [PATCH 171/524] fix: update lints --- clippy_lints/src/declared_lints.rs | 3 ++- clippy_lints/src/lib.rs | 1 - src/docs/unchecked_duration_subtraction.txt | 19 ------------------- 3 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 src/docs/unchecked_duration_subtraction.txt diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 98265410e849..7a705e563fc9 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -202,6 +202,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, + crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, crate::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_INFO, @@ -250,7 +252,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, crate::manual_clamp::MANUAL_CLAMP_INFO, - crate::manual_instant_elapsed::MANUAL_INSTANT_ELAPSED_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ce0e7ebfbf1b..3cfb2f491bc3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -921,7 +921,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); - store.register_late_pass(move || Box::new(unchecked_duration_subtraction::UncheckedDurationSubtraction::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/docs/unchecked_duration_subtraction.txt b/src/docs/unchecked_duration_subtraction.txt deleted file mode 100644 index 15a4c02c6065..000000000000 --- a/src/docs/unchecked_duration_subtraction.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. - -### Why is this bad? -Unchecked subtraction could cause underflow on certain platforms, leading to -unintentional panics. - -### Example -``` -let time_passed = Instant::now() - Duration::from_secs(5); -``` - -Use instead: -``` -let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); -``` - -[`Duration`]: std::time::Duration -[`Instant::now()`]: std::time::Instant::now; \ No newline at end of file From 2bc04bdac26fbb49e3b4ef3673f6a5143df307e7 Mon Sep 17 00:00:00 2001 From: koka Date: Fri, 11 Nov 2022 00:14:18 +0900 Subject: [PATCH 172/524] fix: cognitive_complexity for async fn --- clippy_lints/src/cognitive_complexity.rs | 21 +++++++++++++++------ tests/ui/cognitive_complexity.rs | 8 ++++++++ tests/ui/cognitive_complexity.stderr | 10 +++++++++- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 77af3b53d633..1c3a89a97824 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -4,11 +4,11 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::LimitStack; +use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack}; use core::ops::ControlFlow; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -56,15 +56,13 @@ impl CognitiveComplexity { cx: &LateContext<'tcx>, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, - body: &'tcx Body<'_>, + expr: &'tcx Expr<'_>, body_span: Span, ) { if body_span.from_expansion() { return; } - let expr = body.value; - let mut cc = 1u64; let mut returns = 0u64; let _: Option = for_each_expr(expr, |e| { @@ -146,7 +144,18 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { ) { let def_id = cx.tcx.hir().local_def_id(hir_id); if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) { - self.check(cx, kind, decl, body, span); + let expr = if is_async_fn(kind) { + match get_async_fn_body(cx.tcx, body) { + Some(b) => b, + None => { + return; + }, + } + } else { + body.value + }; + + self.check(cx, kind, decl, expr, span); } } diff --git a/tests/ui/cognitive_complexity.rs b/tests/ui/cognitive_complexity.rs index 912e6788afdd..498a9f0644f5 100644 --- a/tests/ui/cognitive_complexity.rs +++ b/tests/ui/cognitive_complexity.rs @@ -393,3 +393,11 @@ impl Moo { } } } + +#[clippy::cognitive_complexity = "1"] +mod issue9300 { + async fn a() { + let a = 0; + if a == 0 {} + } +} diff --git a/tests/ui/cognitive_complexity.stderr b/tests/ui/cognitive_complexity.stderr index d7f2f24e52f2..208f4d7b3474 100644 --- a/tests/ui/cognitive_complexity.stderr +++ b/tests/ui/cognitive_complexity.stderr @@ -135,5 +135,13 @@ LL | fn moo(&self) { | = help: you could split it up into multiple smaller functions -error: aborting due to 17 previous errors +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:399:14 + | +LL | async fn a() { + | ^ + | + = help: you could split it up into multiple smaller functions + +error: aborting due to 18 previous errors From 3790aa3d5d14ef79b27776767985716844861f33 Mon Sep 17 00:00:00 2001 From: hrxi Date: Thu, 10 Nov 2022 19:42:20 +0100 Subject: [PATCH 173/524] Make `bool_to_int_with_if` a pedantic lint In all the cases I've observed, it did not make the code clearer. Using bools as integer is frowned upon in some languages, in others it's simply not possible. You can find comments on the original pull request #8131 that agree with this point of view. --- clippy_lints/src/bool_to_int_with_if.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 40ded4056696..bdb3a0116027 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// this lint suggests using a `from()` function or an `as` coercion. /// /// ### Why is this bad? - /// Coercion or `from()` is idiomatic way to convert bool to a number. + /// Coercion or `from()` is another way to convert bool to a number. /// Both methods are guaranteed to return 1 for true, and 0 for false. /// /// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E @@ -39,7 +39,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.65.0"] pub BOOL_TO_INT_WITH_IF, - style, + pedantic, "using if to convert bool to int" } declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); From 7ddd321ecd41d8b074d06d21393fed43a85fa150 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 31 Oct 2022 18:30:09 +0000 Subject: [PATCH 174/524] Introduce `ExprKind::IncludedBytes` --- clippy_utils/src/sugg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index aad7da61a8a5..eefba8cd29c4 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -207,6 +207,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::InlineAsm(..) | ast::ExprKind::ConstBlock(..) | ast::ExprKind::Lit(..) + | ast::ExprKind::IncludedBytes(..) | ast::ExprKind::Loop(..) | ast::ExprKind::MacCall(..) | ast::ExprKind::MethodCall(..) From 39398e163ac7508b585f07821e5be195102a4017 Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 12 Nov 2022 19:15:21 +0900 Subject: [PATCH 175/524] fix: use HasPlaceholders * remove unnecessary mutability * fix typo --- clippy_lints/src/manual_is_ascii_check.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 3a6b693f7662..bb8c142f8e46 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -91,15 +91,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { CharRange::Digit => Some("is_ascii_digit"), CharRange::Otherwise => None, } { - let mut applicability = Applicability::MaybeIncorrect; let default_snip = ".."; // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for - // macro span, so we check applicability manually by comaring `recv` is not default. + // macro span, so we check applicability manually by comparing `recv` is not default. let recv = snippet(cx, recv.span, default_snip); - if recv != default_snip { - applicability = Applicability::MachineApplicable; - } + let applicability = if recv == default_snip { + Applicability::HasPlaceholders + } else { + Applicability::MachineApplicable + }; span_lint_and_sugg( cx, From 93edc127a0d9a5ecd403a2f72ff6c02c5fadb696 Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 12 Nov 2022 20:31:25 +0900 Subject: [PATCH 176/524] Avoid lint to unsized mutable reference --- clippy_lints/src/mut_mut.rs | 16 +++++++++------- tests/ui/mut_mut.rs | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index cb16f00047a3..ae17327fd2a6 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -68,13 +68,15 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { expr.span, "generally you want to avoid `&mut &mut _` if possible", ); - } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { - span_lint( - self.cx, - MUT_MUT, - expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", - ); + } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { + if ty.peel_refs().is_sized(self.cx.tcx.at(expr.span), self.cx.param_env) { + span_lint( + self.cx, + MUT_MUT, + expr.span, + "this expression mutably borrows a mutable reference. Consider reborrowing", + ); + } } } } diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index ac8fd9d8fb09..ee3a856566cc 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -57,3 +57,20 @@ fn issue6922() { // do not lint from an external macro mut_mut!(); } + +mod issue9035 { + use std::fmt::Display; + + struct Foo<'a> { + inner: &'a mut dyn Display, + } + + impl Foo<'_> { + fn foo(&mut self) { + let hlp = &mut self.inner; + bar(hlp); + } + } + + fn bar(_: &mut impl Display) {} +} From 34c4520eae7adf1a3adf3fce5376eac2911d08b8 Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 12 Nov 2022 22:36:20 +0900 Subject: [PATCH 177/524] Fix is_async_fn to check FnKind::Method --- clippy_utils/src/lib.rs | 6 +++++- tests/ui/cognitive_complexity.rs | 8 ++++++++ tests/ui/cognitive_complexity.stderr | 10 +++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index f7d3c91777ea..ac4d0b89f17e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1901,7 +1901,11 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { - matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async) + match kind { + FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async, + FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async, + FnKind::Closure => false, + } } /// Peels away all the compiler generated code surrounding the body of an async function, diff --git a/tests/ui/cognitive_complexity.rs b/tests/ui/cognitive_complexity.rs index 498a9f0644f5..07bdaff00dc4 100644 --- a/tests/ui/cognitive_complexity.rs +++ b/tests/ui/cognitive_complexity.rs @@ -400,4 +400,12 @@ mod issue9300 { let a = 0; if a == 0 {} } + + pub struct S; + impl S { + pub async fn async_method() { + let a = 0; + if a == 0 {} + } + } } diff --git a/tests/ui/cognitive_complexity.stderr b/tests/ui/cognitive_complexity.stderr index 208f4d7b3474..5824631fa83b 100644 --- a/tests/ui/cognitive_complexity.stderr +++ b/tests/ui/cognitive_complexity.stderr @@ -143,5 +143,13 @@ LL | async fn a() { | = help: you could split it up into multiple smaller functions -error: aborting due to 18 previous errors +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:406:22 + | +LL | pub async fn async_method() { + | ^^^^^^^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: aborting due to 19 previous errors From 989986144c889051eb3014cc1fdc0891bbb483f5 Mon Sep 17 00:00:00 2001 From: Kartavya Vashishtha Date: Sat, 12 Nov 2022 20:36:30 +0530 Subject: [PATCH 178/524] fix never_loop false positive on unconditional break to internal labeled block ref #9831 --- clippy_lints/src/loops/never_loop.rs | 3 ++- tests/ui/never_loop.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 16b00ad66378..abb18187ef14 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -169,7 +169,8 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { combine_seq(e, arms) } }, - ExprKind::Block(b, _) => never_loop_block(b, main_loop_id), + ExprKind::Block(b, None) => never_loop_block(b, main_loop_id), + ExprKind::Block(b, Some(_label)) => absorb_break(never_loop_block(b, main_loop_id)), ExprKind::Continue(d) => { let id = d .target_id diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 3dbef19890e9..86a5d03f765f 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -229,6 +229,18 @@ pub fn test18() { }; } +// Issue #9831: unconditional break to internal labeled block +pub fn test19() { + fn thing(iter: impl Iterator) { + for _ in iter { + 'b: { + // error goes away if we just have the block's value be (). + break 'b; + } + } + } +} + fn main() { test1(); test2(); From 243661b739b122f51a17e64161d9f3a1324b8d6a Mon Sep 17 00:00:00 2001 From: hrxi Date: Sat, 12 Nov 2022 22:26:09 +0100 Subject: [PATCH 179/524] Make it clear that `or_fun_call` can be a false-positive Also move it to nursery so that the false-positives can be dealt with. CC #8574 --- clippy_lints/src/methods/mod.rs | 22 ++++++++++------------ tests/ui/unwrap_or.rs | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 82a2cc62d820..53f9c355c5d7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -832,32 +832,30 @@ declare_clippy_lint! { /// etc. instead. /// /// ### Why is this bad? - /// The function will always be called and potentially - /// allocate an object acting as the default. + /// The function will always be called. This is only bad if it allocates or + /// does some non-trivial amount of work. /// /// ### Known problems - /// If the function has side-effects, not calling it will - /// change the semantic of the program, but you shouldn't rely on that anyway. + /// If the function has side-effects, not calling it will change the + /// semantic of the program, but you shouldn't rely on that. + /// + /// The lint also cannot figure out whether the function you call is + /// actually expensive to call or not. /// /// ### Example /// ```rust /// # let foo = Some(String::new()); - /// foo.unwrap_or(String::new()); + /// foo.unwrap_or(String::from("empty")); /// ``` /// /// Use instead: /// ```rust /// # let foo = Some(String::new()); - /// foo.unwrap_or_else(String::new); - /// - /// // or - /// - /// # let foo = Some(String::new()); - /// foo.unwrap_or_default(); + /// foo.unwrap_or_else(|| String::from("empty")); /// ``` #[clippy::version = "pre 1.29.0"] pub OR_FUN_CALL, - perf, + nursery, "using any `*or` method with a function call, which suggests `*or_else`" } diff --git a/tests/ui/unwrap_or.rs b/tests/ui/unwrap_or.rs index bfb41e439473..a0c003f5b1ea 100644 --- a/tests/ui/unwrap_or.rs +++ b/tests/ui/unwrap_or.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all)] +#![warn(clippy::all, clippy::or_fun_call)] fn main() { let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); From e6ef47887770fd3414f57f0725b6826bec80031c Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 6 Nov 2022 19:46:55 +0000 Subject: [PATCH 180/524] Store a LocalDefId in hir::Variant & hir::Field. --- clippy_lints/src/manual_non_exhaustive.rs | 6 +++--- clippy_lints/src/missing_doc.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 6806c1466968..4877cee0cc1e 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -157,10 +157,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { && def.variants.len() > 1 { let mut iter = def.variants.iter().filter_map(|v| { - let id = cx.tcx.hir().local_def_id(v.id); - (matches!(v.data, hir::VariantData::Unit(_)) + let id = cx.tcx.hir().local_def_id(v.hir_id); + (matches!(v.data, hir::VariantData::Unit(..)) && v.ident.as_str().starts_with('_') - && is_doc_hidden(cx.tcx.hir().attrs(v.id))) + && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))) .then_some((id, v.span)) }); if let Some((id, span)) = iter.next() diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 2a63681db60e..6fd100762b49 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { - let attrs = cx.tcx.hir().attrs(v.id); + let attrs = cx.tcx.hir().attrs(v.hir_id); if !is_from_proc_macro(cx, v) { self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant"); } From 8a2d0f255d623390c9495dec1c9e5d22bc895e5d Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 13 Nov 2022 22:58:20 +0000 Subject: [PATCH 181/524] Fix clippy and rustdoc please, please, don't match on `Symbol::as_str`s, every time you do, somewhere in the world another waffle becomes sad... --- clippy_utils/src/macros.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 9a682fbe604f..d13b34a66cca 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -199,12 +199,12 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option< pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool { let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false }; matches!( - name.as_str(), - "core_panic_macro" - | "std_panic_macro" - | "core_panic_2015_macro" - | "std_panic_2015_macro" - | "core_panic_2021_macro" + name, + sym::core_panic_macro + | sym::std_panic_macro + | sym::core_panic_2015_macro + | sym::std_panic_2015_macro + | sym::core_panic_2021_macro ) } From b8357ffd1f2b3f7cd12b7cd29d32c08a5ec5b7c5 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Mon, 14 Nov 2022 16:06:21 +0100 Subject: [PATCH 182/524] fix `vec-box-size-threshold` off-by-one error --- clippy_lints/src/types/vec_box.rs | 2 +- tests/ui-toml/vec_box_sized/test.rs | 5 +++-- tests/ui-toml/vec_box_sized/test.stderr | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 6c329d8cdf19..02703ebecb92 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -42,7 +42,7 @@ pub(super) fn check( if !ty_ty.has_escaping_bound_vars(); if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); - if ty_ty_size <= box_size_threshold; + if ty_ty_size < box_size_threshold; then { span_lint_and_sugg( cx, diff --git a/tests/ui-toml/vec_box_sized/test.rs b/tests/ui-toml/vec_box_sized/test.rs index bf04bee16373..4c46deb585b6 100644 --- a/tests/ui-toml/vec_box_sized/test.rs +++ b/tests/ui-toml/vec_box_sized/test.rs @@ -7,8 +7,9 @@ struct C { } struct Foo(Vec>); -struct Bar(Vec>); -struct Baz(Vec>); +struct Bar(Vec>); +struct Quux(Vec>); +struct Baz(Vec>); struct BarBaz(Vec>); struct FooBarBaz(Vec>); diff --git a/tests/ui-toml/vec_box_sized/test.stderr b/tests/ui-toml/vec_box_sized/test.stderr index cf194de3c553..55de68f8ecf4 100644 --- a/tests/ui-toml/vec_box_sized/test.stderr +++ b/tests/ui-toml/vec_box_sized/test.stderr @@ -9,11 +9,11 @@ LL | struct Foo(Vec>); error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/test.rs:10:12 | -LL | struct Bar(Vec>); - | ^^^^^^^^^^^^^ help: try: `Vec` +LL | struct Bar(Vec>); + | ^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/test.rs:13:18 + --> $DIR/test.rs:14:18 | LL | struct FooBarBaz(Vec>); | ^^^^^^^^^^^ help: try: `Vec` From 3f89ab0618a1075f0c53c47d405c0f6e9bf43953 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 14 Nov 2022 22:00:09 +0100 Subject: [PATCH 183/524] chore: update lint version of MANUAL_INSTAN_ELAPSED to 1.65 --- clippy_lints/src/instant_subtraction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index d83beb622cfd..6d23a672856f 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// let prev_instant = Instant::now(); /// let duration = prev_instant.elapsed(); /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub MANUAL_INSTANT_ELAPSED, pedantic, "subtraction between `Instant::now()` and previous `Instant`" From 912dc919af03427288b64921bb4aea0010d071bf Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 14 Nov 2022 22:08:11 +0100 Subject: [PATCH 184/524] docs: update unchecked duration subtraction lint doc --- clippy_lints/src/instant_subtraction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 6d23a672856f..3f72222f31b6 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -41,7 +41,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`]. + /// Lints subtraction between an [`Instant`] and a [`Duration`]. /// /// ### Why is this bad? /// Unchecked subtraction could cause underflow on certain platforms, leading to From 72ab91d8066ee399cff3e3c8293bf4dbccfb6358 Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 14 Nov 2022 22:25:56 +0100 Subject: [PATCH 185/524] fix: add extract_msrv_attr call to instan_subtraction lint pass --- clippy_lints/src/instant_subtraction.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 3f72222f31b6..716c11becd9c 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -112,6 +112,8 @@ impl LateLintPass<'_> for InstantSubtraction { } } } + + extract_msrv_attr!(LateContext); } fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { From 0dd6ce0b1931e3e5c274daf4bbf50e96a322befd Mon Sep 17 00:00:00 2001 From: Nadir Fejzic Date: Mon, 14 Nov 2022 22:37:25 +0100 Subject: [PATCH 186/524] docs: import Instant and Duration in doctests --- clippy_lints/src/instant_subtraction.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 716c11becd9c..60754b224fc8 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -49,11 +49,13 @@ declare_clippy_lint! { /// /// ### Example /// ```rust + /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now() - Duration::from_secs(5); /// ``` /// /// Use instead: /// ```rust + /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); /// ``` /// From 7c4611c7e170a7eb6a642f9267b95a14f0cd54de Mon Sep 17 00:00:00 2001 From: koka Date: Tue, 15 Nov 2022 12:58:17 +0900 Subject: [PATCH 187/524] Allow return types for closures with lifetime binder --- clippy_lints/src/unused_unit.rs | 8 +++++-- tests/ui/unused_unit.fixed | 7 ++++++ tests/ui/unused_unit.rs | 7 ++++++ tests/ui/unused_unit.stderr | 40 ++++++++++++++++----------------- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index cd1d90e860b9..cad8da18c2fb 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{position_before_rarrow, snippet_opt}; use if_chain::if_chain; -use rustc_ast::ast; -use rustc_ast::visit::FnKind; +use rustc_ast::{ast, visit::FnKind, ClosureBinder}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -43,6 +42,11 @@ impl EarlyLintPass for UnusedUnit { if let ast::TyKind::Tup(ref vals) = ty.kind; if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); then { + // implicit types in closure signatures are forbidden when `for<...>` is present + if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + return; + } + lint_unneeded_unit_return(cx, ty, span); } } diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 7bb43cf7ae82..3dd640b86f0b 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -7,6 +7,7 @@ // test of the JSON error format. #![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] #![rustfmt::skip] #![deny(clippy::unused_unit)] @@ -87,3 +88,9 @@ fn macro_expr() { } e!() } + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 21073fb802ad..bddecf06fb76 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -7,6 +7,7 @@ // test of the JSON error format. #![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] #![rustfmt::skip] #![deny(clippy::unused_unit)] @@ -87,3 +88,9 @@ fn macro_expr() { } e!() } + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index 0d2cb77855be..ce06738cfe47 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,119 +1,119 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:58 + --> $DIR/unused_unit.rs:20:58 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` | note: the lint level is defined here - --> $DIR/unused_unit.rs:12:9 + --> $DIR/unused_unit.rs:13:9 | LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:28 + --> $DIR/unused_unit.rs:20:28 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:18 + --> $DIR/unused_unit.rs:21:18 | LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:21:26 + --> $DIR/unused_unit.rs:22:26 | LL | let _y: &dyn Fn() -> () = &f; | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:28:18 + --> $DIR/unused_unit.rs:29:18 | LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:29:9 + --> $DIR/unused_unit.rs:30:9 | LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:34:29 + --> $DIR/unused_unit.rs:35:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:36:19 + --> $DIR/unused_unit.rs:37:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:37:16 + --> $DIR/unused_unit.rs:38:16 | LL | H: Fn() -> (); | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:41:29 + --> $DIR/unused_unit.rs:42:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:43:19 + --> $DIR/unused_unit.rs:44:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:44:16 + --> $DIR/unused_unit.rs:45:16 | LL | H: Fn() -> () {} | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:47:17 + --> $DIR/unused_unit.rs:48:17 | LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:47:26 + --> $DIR/unused_unit.rs:48:26 | LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:57:14 + --> $DIR/unused_unit.rs:58:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:59:11 + --> $DIR/unused_unit.rs:60:11 | LL | return(); | ^^ help: remove the `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:76:10 + --> $DIR/unused_unit.rs:77:10 | LL | fn test()->(){} | ^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:79:11 + --> $DIR/unused_unit.rs:80:11 | LL | fn test2() ->(){} | ^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:82:11 + --> $DIR/unused_unit.rs:83:11 | LL | fn test3()-> (){} | ^^^^^ help: remove the `-> ()` From 93ac0f58bfeb8046a6e49b79fbaca0cb27d92f56 Mon Sep 17 00:00:00 2001 From: Aphek Date: Tue, 15 Nov 2022 01:57:56 -0300 Subject: [PATCH 188/524] Keep `ref` on `infallible_destructuring_match` suggestion --- .../matches/infallible_destructuring_match.rs | 7 ++++--- tests/ui/infallible_destructuring_match.fixed | 12 ++++++++++++ tests/ui/infallible_destructuring_match.rs | 14 ++++++++++++++ tests/ui/infallible_destructuring_match.stderr | 16 ++++++++++++---- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/matches/infallible_destructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs index 2472acb6f6e8..d18c92caba2a 100644 --- a/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath}; +use rustc_hir::{ByRef, ExprKind, Local, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; use super::INFALLIBLE_DESTRUCTURING_MATCH; @@ -16,7 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { if let PatKind::TupleStruct( QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; if args.len() == 1; - if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind; + if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind; let body = peel_blocks(arms[0].body); if path_to_local_id(body, arg); @@ -30,8 +30,9 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { Consider using `let`", "try this", format!( - "let {}({}) = {};", + "let {}({}{}) = {};", snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), + if binding.0 == ByRef::Yes { "ref " } else { "" }, snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), snippet_with_applicability(cx, target.span, "..", &mut applicability), ), diff --git a/tests/ui/infallible_destructuring_match.fixed b/tests/ui/infallible_destructuring_match.fixed index b8e40d995531..61985e56b769 100644 --- a/tests/ui/infallible_destructuring_match.fixed +++ b/tests/ui/infallible_destructuring_match.fixed @@ -9,6 +9,9 @@ enum SingleVariantEnum { struct TupleStruct(i32); +struct NonCopy; +struct TupleStructWithNonCopy(NonCopy); + enum EmptyEnum {} macro_rules! match_enum { @@ -71,6 +74,15 @@ fn infallible_destructuring_match_struct() { let TupleStruct(data) = wrapper; } +fn infallible_destructuring_match_struct_with_noncopy() { + let wrapper = TupleStructWithNonCopy(NonCopy); + + // This should lint! (keeping `ref` in the suggestion) + let TupleStructWithNonCopy(ref data) = wrapper; + + let TupleStructWithNonCopy(ref data) = wrapper; +} + macro_rules! match_never_enum { ($param:expr) => { let data = match $param { diff --git a/tests/ui/infallible_destructuring_match.rs b/tests/ui/infallible_destructuring_match.rs index 106cd438b90e..f2768245bbc4 100644 --- a/tests/ui/infallible_destructuring_match.rs +++ b/tests/ui/infallible_destructuring_match.rs @@ -9,6 +9,9 @@ enum SingleVariantEnum { struct TupleStruct(i32); +struct NonCopy; +struct TupleStructWithNonCopy(NonCopy); + enum EmptyEnum {} macro_rules! match_enum { @@ -75,6 +78,17 @@ fn infallible_destructuring_match_struct() { let TupleStruct(data) = wrapper; } +fn infallible_destructuring_match_struct_with_noncopy() { + let wrapper = TupleStructWithNonCopy(NonCopy); + + // This should lint! (keeping `ref` in the suggestion) + let data = match wrapper { + TupleStructWithNonCopy(ref n) => n, + }; + + let TupleStructWithNonCopy(ref data) = wrapper; +} + macro_rules! match_never_enum { ($param:expr) => { let data = match $param { diff --git a/tests/ui/infallible_destructuring_match.stderr b/tests/ui/infallible_destructuring_match.stderr index 1b78db42014a..f8a50f0223d6 100644 --- a/tests/ui/infallible_destructuring_match.stderr +++ b/tests/ui/infallible_destructuring_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:26:5 + --> $DIR/infallible_destructuring_match.rs:29:5 | LL | / let data = match wrapper { LL | | SingleVariantEnum::Variant(i) => i, @@ -9,7 +9,7 @@ LL | | }; = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings` error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:58:5 + --> $DIR/infallible_destructuring_match.rs:61:5 | LL | / let data = match wrapper { LL | | TupleStruct(i) => i, @@ -17,12 +17,20 @@ LL | | }; | |______^ help: try this: `let TupleStruct(data) = wrapper;` error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:90:5 + --> $DIR/infallible_destructuring_match.rs:85:5 + | +LL | / let data = match wrapper { +LL | | TupleStructWithNonCopy(ref n) => n, +LL | | }; + | |______^ help: try this: `let TupleStructWithNonCopy(ref data) = wrapper;` + +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` + --> $DIR/infallible_destructuring_match.rs:104:5 | LL | / let data = match wrapper { LL | | Ok(i) => i, LL | | }; | |______^ help: try this: `let Ok(data) = wrapper;` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From f75fc857839ff71e89d968bb1952e54a3969fb9f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 15 Nov 2022 18:24:18 +0000 Subject: [PATCH 189/524] Extend `needless_borrowed_reference` to structs and tuples, ignore _ --- clippy_lints/src/enum_variants.rs | 2 +- clippy_lints/src/int_plus_one.rs | 28 ++--- clippy_lints/src/len_zero.rs | 3 +- clippy_lints/src/needless_borrowed_ref.rs | 108 +++++++++++------ clippy_lints/src/unsafe_removed_from_name.rs | 2 +- clippy_utils/src/consts.rs | 22 ++-- clippy_utils/src/hir_utils.rs | 16 ++- tests/ui/match_expr_like_matches_macro.fixed | 7 +- tests/ui/match_expr_like_matches_macro.rs | 7 +- tests/ui/match_expr_like_matches_macro.stderr | 28 ++--- tests/ui/needless_borrowed_ref.fixed | 67 ++++++++++- tests/ui/needless_borrowed_ref.rs | 67 ++++++++++- tests/ui/needless_borrowed_ref.stderr | 113 ++++++++++++++++-- 13 files changed, 356 insertions(+), 114 deletions(-) diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index b019d07d53d1..7add358e8e4e 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -250,7 +250,7 @@ impl LateLintPass<'_> for EnumVariantNames { let item_name = item.ident.name.as_str(); let item_camel = to_camel_case(item_name); if !item.span.from_expansion() && is_present_in_source(cx, item.span) { - if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() { + if let Some((mod_name, mod_camel)) = self.modules.last() { // constants don't have surrounding modules if !mod_camel.is_empty() { if mod_name == &item.ident.name { diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 33491da3fc5a..51757592c02d 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -62,58 +62,54 @@ impl IntPlusOne { fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option { match (binop, &lhs.kind, &rhs.kind) { // case where `x - 1 >= ...` or `-1 + x >= ...` - (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => { + (BinOpKind::Ge, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) => { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { // `-1 + x` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, // `x - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } }, // case where `... >= y + 1` or `... >= 1 + y` - (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) - if rhskind.node == BinOpKind::Add => - { + (BinOpKind::Ge, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) if rhskind.node == BinOpKind::Add => { match (&rhslhs.kind, &rhsrhs.kind) { // `y + 1` and `1 + y` - (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + (ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, } }, // case where `x + 1 <= ...` or `1 + x <= ...` - (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) - if lhskind.node == BinOpKind::Add => - { + (BinOpKind::Le, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) if lhskind.node == BinOpKind::Add => { match (&lhslhs.kind, &lhsrhs.kind) { // `1 + x` and `x + 1` - (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + (ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } }, // case where `... >= y - 1` or `... >= -1 + y` - (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => { + (BinOpKind::Le, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) => { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { // `-1 + y` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, // `y - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 3a563736fb07..76b0c21327c8 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -366,8 +366,7 @@ fn check_for_is_empty<'tcx>( } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { - if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) - { + if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method if let Some(name) = get_item_name(cx, method) { if name.as_str() == "is_empty" { diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 10c3ff026b6d..498e1408e52a 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -36,14 +36,14 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { - fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - if pat.span.from_expansion() { + fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) { + if ref_pat.span.from_expansion() { // OK, simple enough, lints doesn't check in macro. return; } // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms - for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) { + for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) { let Node::Pat(pat) = node else { break }; if matches!(pat.kind, PatKind::Or(_)) { @@ -52,20 +52,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { } // Only lint immutable refs, because `&mut ref T` may be useful. - let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return }; + let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { return }; - match sub_pat.kind { + match pat.kind { // Check sub_pat got a `ref` keyword (excluding `ref mut`). PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { span_lint_and_then( cx, NEEDLESS_BORROWED_REFERENCE, - pat.span, + ref_pat.span, "this pattern takes a reference on something that is being dereferenced", |diag| { // `&ref ident` // ^^^^^ - let span = pat.span.until(ident.span); + let span = ref_pat.span.until(ident.span); diag.span_suggestion_verbose( span, "try removing the `&ref` part", @@ -84,41 +84,71 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { }), after, ) => { - let mut suggestions = Vec::new(); - - for element_pat in itertools::chain(before, after) { - if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind { - // `&[..., ref ident, ...]` - // ^^^^ - let span = element_pat.span.until(ident.span); - suggestions.push((span, String::new())); - } else { - return; - } - } + check_subpatterns( + cx, + "dereferencing a slice pattern where every element takes a reference", + ref_pat, + pat, + itertools::chain(before, after), + ); + }, + PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { + check_subpatterns( + cx, + "dereferencing a tuple pattern where every element takes a reference", + ref_pat, + pat, + subpatterns, + ); + }, + PatKind::Struct(_, fields, _) => { + check_subpatterns( + cx, + "dereferencing a struct pattern where every field's pattern takes a reference", + ref_pat, + pat, + fields.iter().map(|field| field.pat), + ); + }, + _ => {}, + } + } +} - if !suggestions.is_empty() { - span_lint_and_then( - cx, - NEEDLESS_BORROWED_REFERENCE, - pat.span, - "dereferencing a slice pattern where every element takes a reference", - |diag| { - // `&[...]` - // ^ - let span = pat.span.until(sub_pat.span); - suggestions.push((span, String::new())); +fn check_subpatterns<'tcx>( + cx: &LateContext<'tcx>, + message: &str, + ref_pat: &Pat<'_>, + pat: &Pat<'_>, + subpatterns: impl IntoIterator>, +) { + let mut suggestions = Vec::new(); - diag.multipart_suggestion( - "try removing the `&` and `ref` parts", - suggestions, - Applicability::MachineApplicable, - ); - }, - ); - } + for subpattern in subpatterns { + match subpattern.kind { + PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { + // `ref ident` + // ^^^^ + let span = subpattern.span.until(ident.span); + suggestions.push((span, String::new())); }, - _ => {}, + PatKind::Wild => {}, + _ => return, } } + + if !suggestions.is_empty() { + span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, ref_pat.span, message, |diag| { + // `&pat` + // ^ + let span = ref_pat.span.until(pat.span); + suggestions.push((span, String::new())); + + diag.multipart_suggestion( + "try removing the `&` and `ref` parts", + suggestions, + Applicability::MachineApplicable, + ); + }); + } } diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 32cd46812014..952586527689 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -50,7 +50,7 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { }, UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {}, UseTreeKind::Nested(ref nested_use_tree) => { - for &(ref use_tree, _) in nested_use_tree { + for (use_tree, _) in nested_use_tree { check_use_tree(use_tree, cx, span); } }, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index f01afda72b2c..315aea9aa091 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -51,8 +51,8 @@ pub enum Constant { impl PartialEq for Constant { fn eq(&self, other: &Self) -> bool { match (self, other) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs, - (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r, + (Self::Str(ls), Self::Str(rs)) => ls == rs, + (Self::Binary(l), Self::Binary(r)) => l == r, (&Self::Char(l), &Self::Char(r)) => l == r, (&Self::Int(l), &Self::Int(r)) => l == r, (&Self::F64(l), &Self::F64(r)) => { @@ -69,8 +69,8 @@ impl PartialEq for Constant { }, (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, - (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb, + (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => ls == rs && lv == rv, + (Self::Ref(lb), Self::Ref(rb)) => *lb == *rb, // TODO: are there inter-type equalities? _ => false, } @@ -126,8 +126,8 @@ impl Hash for Constant { impl Constant { pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { match (left, right) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), - (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), + (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)), + (Self::Char(l), Self::Char(r)) => Some(l.cmp(r)), (&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() { ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))), ty::Uint(_) => Some(l.cmp(&r)), @@ -135,8 +135,8 @@ impl Constant { }, (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), - (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), - (&Self::Tuple(ref l), &Self::Tuple(ref r)) if l.len() == r.len() => match *cmp_type.kind() { + (Self::Bool(l), Self::Bool(r)) => Some(l.cmp(r)), + (Self::Tuple(l), Self::Tuple(r)) if l.len() == r.len() => match *cmp_type.kind() { ty::Tuple(tys) if tys.len() == l.len() => l .iter() .zip(r) @@ -146,7 +146,7 @@ impl Constant { .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), _ => None, }, - (&Self::Vec(ref l), &Self::Vec(ref r)) => { + (Self::Vec(l), Self::Vec(r)) => { let (ty::Array(cmp_type, _) | ty::Slice(cmp_type)) = *cmp_type.kind() else { return None }; @@ -155,7 +155,7 @@ impl Constant { .find(|r| r.map_or(true, |o| o != Ordering::Equal)) .unwrap_or_else(|| Some(l.len().cmp(&r.len()))) }, - (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { + (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => { match Self::partial_cmp( tcx, match *cmp_type.kind() { @@ -169,7 +169,7 @@ impl Constant { x => x, } }, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp( + (Self::Ref(lb), Self::Ref(rb)) => Self::partial_cmp( tcx, match *cmp_type.kind() { ty::Ref(_, ty, _) => ty, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index da8c976c952c..0231a51adf48 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -266,7 +266,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Let(l), &ExprKind::Let(r)) => { self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) }, - (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, + (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node, (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) }, @@ -291,8 +291,8 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => { self.eq_expr(le, re) && self.eq_array_length(ll, rl) }, - (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)), - (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r), + (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l, r, |l, r| self.eq_expr(l, r)), + (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r), (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { self.eq_qpath(l_path, r_path) && both(lo, ro, |l, r| self.eq_expr(l, r)) @@ -362,7 +362,7 @@ impl HirEqInterExpr<'_, '_, '_> { } eq }, - (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), + (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { @@ -429,13 +429,11 @@ impl HirEqInterExpr<'_, '_, '_> { match (&left.kind, &right.kind) { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), - (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { - l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty) - }, - (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => { + (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty), + (TyKind::Rptr(_, l_rmut), TyKind::Rptr(_, r_rmut)) => { l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, - (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r), + (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), (&TyKind::Infer, &TyKind::Infer) => true, _ => false, diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 2498007694c5..968f462f8a02 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -2,7 +2,12 @@ #![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] +#![allow( + unreachable_patterns, + dead_code, + clippy::equatable_if_let, + clippy::needless_borrowed_reference +)] fn main() { let x = Some(5); diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index b4e48499bd0f..c6b479e27c5a 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -2,7 +2,12 @@ #![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] +#![allow( + unreachable_patterns, + dead_code, + clippy::equatable_if_let, + clippy::needless_borrowed_reference +)] fn main() { let x = Some(5); diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index f1d1c23aeb0d..a4df8008ac23 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:11:14 + --> $DIR/match_expr_like_matches_macro.rs:16:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:17:14 + --> $DIR/match_expr_like_matches_macro.rs:22:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:23:14 + --> $DIR/match_expr_like_matches_macro.rs:28:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:29:15 + --> $DIR/match_expr_like_matches_macro.rs:34:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:35:16 + --> $DIR/match_expr_like_matches_macro.rs:40:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:59:20 + --> $DIR/match_expr_like_matches_macro.rs:64:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:69:20 + --> $DIR/match_expr_like_matches_macro.rs:74:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:79:20 + --> $DIR/match_expr_like_matches_macro.rs:84:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:139:18 + --> $DIR/match_expr_like_matches_macro.rs:144:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:148:18 + --> $DIR/match_expr_like_matches_macro.rs:153:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:165:21 + --> $DIR/match_expr_like_matches_macro.rs:170:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:179:20 + --> $DIR/match_expr_like_matches_macro.rs:184:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:191:20 + --> $DIR/match_expr_like_matches_macro.rs:196:20 | LL | let _res = match &val { | ____________________^ @@ -134,7 +134,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:251:14 + --> $DIR/match_expr_like_matches_macro.rs:256:14 | LL | let _y = match Some(5) { | ______________^ diff --git a/tests/ui/needless_borrowed_ref.fixed b/tests/ui/needless_borrowed_ref.fixed index bcb4eb2dd48a..0c47ceb7b679 100644 --- a/tests/ui/needless_borrowed_ref.fixed +++ b/tests/ui/needless_borrowed_ref.fixed @@ -1,11 +1,32 @@ // run-rustfix #![warn(clippy::needless_borrowed_reference)] -#![allow(unused, clippy::needless_borrow)] +#![allow( + unused, + irrefutable_let_patterns, + non_shorthand_field_patterns, + clippy::needless_borrow +)] fn main() {} -fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +struct Struct { + a: usize, + b: usize, + c: usize, +} + +struct TupleStruct(u8, u8, u8); + +fn should_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|a| a.is_empty()); @@ -24,16 +45,54 @@ fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec if let [a, b, ..] = slice {} if let [a, .., b] = slice {} if let [.., a, b] = slice {} + + if let [a, _] = slice {} + + if let (a, b, c) = &tuple {} + if let (a, _, c) = &tuple {} + if let (a, ..) = &tuple {} + + if let TupleStruct(a, ..) = &tuple_struct {} + + if let Struct { + a, + b: b, + c: renamed, + } = &s + {} + + if let Struct { a, b: _, .. } = &s {} } -fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +fn should_not_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { if let [ref a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} + if let &(ref a, b, ..) = &tuple {} + if let &TupleStruct(ref a, b, ..) = &tuple_struct {} + if let &Struct { ref a, b, .. } = &s {} + // must not be removed as variables must be bound consistently across | patterns if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} + // the `&`s here technically could be removed, but it'd be noisy and without a `ref` doesn't match + // the lint name + if let &[] = slice {} + if let &[_] = slice {} + if let &[..] = slice {} + if let &(..) = &tuple {} + if let &TupleStruct(..) = &tuple_struct {} + if let &Struct { .. } = &s {} + let mut var2 = 5; let thingy2 = Some(&mut var2); if let Some(&mut ref mut v) = thingy2 { @@ -59,6 +118,6 @@ fn foo(a: &Animal, b: &Animal) { // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted - (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + (Animal::Dog(a), &Animal::Dog(_)) => (), } } diff --git a/tests/ui/needless_borrowed_ref.rs b/tests/ui/needless_borrowed_ref.rs index f6de1a6d83d1..f883bb0c8891 100644 --- a/tests/ui/needless_borrowed_ref.rs +++ b/tests/ui/needless_borrowed_ref.rs @@ -1,11 +1,32 @@ // run-rustfix #![warn(clippy::needless_borrowed_reference)] -#![allow(unused, clippy::needless_borrow)] +#![allow( + unused, + irrefutable_let_patterns, + non_shorthand_field_patterns, + clippy::needless_borrow +)] fn main() {} -fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +struct Struct { + a: usize, + b: usize, + c: usize, +} + +struct TupleStruct(u8, u8, u8); + +fn should_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|&ref a| a.is_empty()); @@ -24,16 +45,54 @@ fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec if let &[ref a, ref b, ..] = slice {} if let &[ref a, .., ref b] = slice {} if let &[.., ref a, ref b] = slice {} + + if let &[ref a, _] = slice {} + + if let &(ref a, ref b, ref c) = &tuple {} + if let &(ref a, _, ref c) = &tuple {} + if let &(ref a, ..) = &tuple {} + + if let &TupleStruct(ref a, ..) = &tuple_struct {} + + if let &Struct { + ref a, + b: ref b, + c: ref renamed, + } = &s + {} + + if let &Struct { ref a, b: _, .. } = &s {} } -fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +fn should_not_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { if let [ref a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} + if let &(ref a, b, ..) = &tuple {} + if let &TupleStruct(ref a, b, ..) = &tuple_struct {} + if let &Struct { ref a, b, .. } = &s {} + // must not be removed as variables must be bound consistently across | patterns if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} + // the `&`s here technically could be removed, but it'd be noisy and without a `ref` doesn't match + // the lint name + if let &[] = slice {} + if let &[_] = slice {} + if let &[..] = slice {} + if let &(..) = &tuple {} + if let &TupleStruct(..) = &tuple_struct {} + if let &Struct { .. } = &s {} + let mut var2 = 5; let thingy2 = Some(&mut var2); if let Some(&mut ref mut v) = thingy2 { @@ -59,6 +118,6 @@ fn foo(a: &Animal, b: &Animal) { // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted - (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + (Animal::Dog(a), &Animal::Dog(_)) => (), } } diff --git a/tests/ui/needless_borrowed_ref.stderr b/tests/ui/needless_borrowed_ref.stderr index 7453542e673f..8d0f0c258dd2 100644 --- a/tests/ui/needless_borrowed_ref.stderr +++ b/tests/ui/needless_borrowed_ref.stderr @@ -1,5 +1,5 @@ error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:10:34 + --> $DIR/needless_borrowed_ref.rs:31:34 | LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); | ^^^^^^ @@ -12,7 +12,7 @@ LL + let _ = v.iter_mut().filter(|a| a.is_empty()); | error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:14:17 + --> $DIR/needless_borrowed_ref.rs:35:17 | LL | if let Some(&ref v) = thingy {} | ^^^^^^ @@ -24,7 +24,7 @@ LL + if let Some(v) = thingy {} | error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:16:14 + --> $DIR/needless_borrowed_ref.rs:37:14 | LL | if let &[&ref a, ref b] = slice_of_refs {} | ^^^^^^ @@ -36,7 +36,7 @@ LL + if let &[a, ref b] = slice_of_refs {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:18:9 + --> $DIR/needless_borrowed_ref.rs:39:9 | LL | let &[ref a, ..] = &array; | ^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + let [a, ..] = &array; | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:19:9 + --> $DIR/needless_borrowed_ref.rs:40:9 | LL | let &[ref a, ref b, ..] = &array; | ^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL + let [a, b, ..] = &array; | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:21:12 + --> $DIR/needless_borrowed_ref.rs:42:12 | LL | if let &[ref a, ref b] = slice {} | ^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL + if let [a, b] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:22:12 + --> $DIR/needless_borrowed_ref.rs:43:12 | LL | if let &[ref a, ref b] = &vec[..] {} | ^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL + if let [a, b] = &vec[..] {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:24:12 + --> $DIR/needless_borrowed_ref.rs:45:12 | LL | if let &[ref a, ref b, ..] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL + if let [a, b, ..] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:25:12 + --> $DIR/needless_borrowed_ref.rs:46:12 | LL | if let &[ref a, .., ref b] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL + if let [a, .., b] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:26:12 + --> $DIR/needless_borrowed_ref.rs:47:12 | LL | if let &[.., ref a, ref b] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -119,5 +119,96 @@ LL - if let &[.., ref a, ref b] = slice {} LL + if let [.., a, b] = slice {} | -error: aborting due to 10 previous errors +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:49:12 + | +LL | if let &[ref a, _] = slice {} + | ^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, _] = slice {} +LL + if let [a, _] = slice {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:51:12 + | +LL | if let &(ref a, ref b, ref c) = &tuple {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, ref b, ref c) = &tuple {} +LL + if let (a, b, c) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:52:12 + | +LL | if let &(ref a, _, ref c) = &tuple {} + | ^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, _, ref c) = &tuple {} +LL + if let (a, _, c) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:53:12 + | +LL | if let &(ref a, ..) = &tuple {} + | ^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, ..) = &tuple {} +LL + if let (a, ..) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:55:12 + | +LL | if let &TupleStruct(ref a, ..) = &tuple_struct {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &TupleStruct(ref a, ..) = &tuple_struct {} +LL + if let TupleStruct(a, ..) = &tuple_struct {} + | + +error: dereferencing a struct pattern where every field's pattern takes a reference + --> $DIR/needless_borrowed_ref.rs:57:12 + | +LL | if let &Struct { + | ____________^ +LL | | ref a, +LL | | b: ref b, +LL | | c: ref renamed, +LL | | } = &s + | |_____^ + | +help: try removing the `&` and `ref` parts + | +LL ~ if let Struct { +LL ~ a, +LL ~ b: b, +LL ~ c: renamed, + | + +error: dereferencing a struct pattern where every field's pattern takes a reference + --> $DIR/needless_borrowed_ref.rs:64:12 + | +LL | if let &Struct { ref a, b: _, .. } = &s {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &Struct { ref a, b: _, .. } = &s {} +LL + if let Struct { a, b: _, .. } = &s {} + | + +error: aborting due to 17 previous errors From f2d83ed1ac7254d943975fa57e54e6013d16eecf Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 10 Oct 2022 13:40:56 +1100 Subject: [PATCH 190/524] Use `token::Lit` in `ast::ExprKind::Lit`. Instead of `ast::Lit`. Literal lowering now happens at two different times. Expression literals are lowered when HIR is crated. Attribute literals are lowered during parsing. This commit changes the language very slightly. Some programs that used to not compile now will compile. This is because some invalid literals that are removed by `cfg` or attribute macros will no longer trigger errors. See this comment for more details: https://github.com/rust-lang/rust/pull/102944#issuecomment-1277476773 --- .../src/almost_complete_letter_range.rs | 19 +++++++--- clippy_lints/src/int_plus_one.rs | 23 ++++++------ clippy_lints/src/literal_representation.rs | 36 ++++++++++--------- clippy_lints/src/misc_early/literal_suffix.rs | 8 ++--- .../src/misc_early/mixed_case_hex_literals.rs | 6 ++-- clippy_lints/src/misc_early/mod.rs | 24 +++++++------ .../src/misc_early/zero_prefixed_literal.rs | 10 +++--- clippy_lints/src/octal_escapes.rs | 10 +++--- clippy_lints/src/precedence.rs | 5 +-- clippy_lints/src/unused_rounding.rs | 18 +++++----- clippy_utils/src/ast_utils.rs | 2 +- clippy_utils/src/numeric_literal.rs | 6 +--- 12 files changed, 90 insertions(+), 77 deletions(-) diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_letter_range.rs index 073e4af1318e..df92579a85df 100644 --- a/clippy_lints/src/almost_complete_letter_range.rs +++ b/clippy_lints/src/almost_complete_letter_range.rs @@ -73,12 +73,21 @@ impl EarlyLintPass for AlmostCompleteLetterRange { } fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) { - if let ExprKind::Lit(start_lit) = &start.peel_parens().kind - && let ExprKind::Lit(end_lit) = &end.peel_parens().kind + if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind + && let ExprKind::Lit(end_token_lit) = end.peel_parens().kind && matches!( - (&start_lit.kind, &end_lit.kind), - (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z')) - | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z')) + ( + LitKind::from_token_lit(start_token_lit), + LitKind::from_token_lit(end_token_lit), + ), + ( + Ok(LitKind::Byte(b'a') | LitKind::Char('a')), + Ok(LitKind::Byte(b'z') | LitKind::Char('z')) + ) + | ( + Ok(LitKind::Byte(b'A') | LitKind::Char('A')), + Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')), + ) ) && !in_external_macro(cx.sess(), span) { diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 33491da3fc5a..f793abdfda34 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind}; +use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -52,8 +53,8 @@ enum Side { impl IntPlusOne { #[expect(clippy::cast_sign_loss)] - fn check_lit(lit: &Lit, target_value: i128) -> bool { - if let LitKind::Int(value, ..) = lit.kind { + fn check_lit(token_lit: token::Lit, target_value: i128) -> bool { + if let Ok(LitKind::Int(value, ..)) = LitKind::from_token_lit(token_lit) { return value == (target_value as u128); } false @@ -65,11 +66,11 @@ impl IntPlusOne { (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { // `-1 + x` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, // `x - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, @@ -81,10 +82,10 @@ impl IntPlusOne { { match (&rhslhs.kind, &rhsrhs.kind) { // `y + 1` and `1 + y` - (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, @@ -96,10 +97,10 @@ impl IntPlusOne { { match (&lhslhs.kind, &lhsrhs.kind) { // `1 + x` and `x + 1` - (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, @@ -109,11 +110,11 @@ impl IntPlusOne { (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { // `-1 + y` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, // `y - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 25f19b9c6e6c..3a7b7835c990 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -5,11 +5,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; +use rustc_ast::ast::{Expr, ExprKind, LitKind}; +use rustc_ast::token; 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_span::Span; use std::iter; declare_clippy_lint! { @@ -236,8 +238,8 @@ impl EarlyLintPass for LiteralDigitGrouping { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - self.check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + self.check_lit(cx, lit, expr.span); } } } @@ -252,12 +254,13 @@ impl LiteralDigitGrouping { } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { if_chain! { - if let Some(src) = snippet_opt(cx, lit.span); - if let Some(mut num_lit) = NumericLiteral::from_lit(&src, lit); + if let Some(src) = snippet_opt(cx, span); + if let Ok(lit_kind) = LitKind::from_token_lit(lit); + if let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); then { - if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) { + if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) { return; } @@ -293,14 +296,14 @@ impl LiteralDigitGrouping { | WarningType::InconsistentDigitGrouping | WarningType::UnusualByteGroupings | WarningType::LargeDigitGroups => { - !lit.span.from_expansion() + !span.from_expansion() } WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => { true } }; if should_warn { - warning_type.display(num_lit.format(), cx, lit.span); + warning_type.display(num_lit.format(), cx, span); } } } @@ -458,8 +461,8 @@ impl EarlyLintPass for DecimalLiteralRepresentation { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - self.check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + self.check_lit(cx, lit, expr.span); } } } @@ -469,19 +472,20 @@ impl DecimalLiteralRepresentation { pub fn new(threshold: u64) -> Self { Self { threshold } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // Lint integral literals. if_chain! { - if let LitKind::Int(val, _) = lit.kind; - if let Some(src) = snippet_opt(cx, lit.span); - if let Some(num_lit) = NumericLiteral::from_lit(&src, lit); + if let Ok(lit_kind) = LitKind::from_token_lit(lit); + if let LitKind::Int(val, _) = lit_kind; + if let Some(src) = snippet_opt(cx, span); + if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); if num_lit.radix == Radix::Decimal; if val >= u128::from(self.threshold); then { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { - warning_type.display(num_lit.format(), cx, lit.span); + warning_type.display(num_lit.format(), cx, span); }); } } diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs index 27e7f8505eb5..eda4376f200e 100644 --- a/clippy_lints/src/misc_early/literal_suffix.rs +++ b/clippy_lints/src/misc_early/literal_suffix.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::Lit; use rustc_errors::Applicability; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::{SEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX}; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str, suffix: &str, sugg_type: &str) { let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; @@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s span_lint_and_sugg( cx, SEPARATED_LITERAL_SUFFIX, - lit.span, + lit_span, &format!("{sugg_type} type suffix should not be separated by an underscore"), "remove the underscore", format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), @@ -25,7 +25,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s span_lint_and_sugg( cx, UNSEPARATED_LITERAL_SUFFIX, - lit.span, + lit_span, &format!("{sugg_type} type suffix should be separated by an underscore"), "add an underscore", format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 263ee1e945a2..ddb8b9173a53 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::Lit; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::MIXED_CASE_HEX_LITERALS; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, suffix: &str, lit_snip: &str) { let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; @@ -23,7 +23,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &s span_lint( cx, MIXED_CASE_HEX_LITERALS, - lit.span, + lit_span, "inconsistent casing in hexadecimal literal", ); break; diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index c8227ca44505..78be6b9e23fa 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -9,7 +9,8 @@ mod zero_prefixed_literal; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; +use rustc_ast::ast::{Expr, ExprKind, Generics, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; +use rustc_ast::token; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -374,42 +375,43 @@ impl EarlyLintPass for MiscEarlyLints { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - MiscEarlyLints::check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + MiscEarlyLints::check_lit(cx, lit, expr.span); } double_neg::check(cx, expr); } } impl MiscEarlyLints { - fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // We test if first character in snippet is a number, because the snippet could be an expansion // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`. // Note that this check also covers special case that `line!()` is eagerly expanded by compiler. // See for a regression. // FIXME: Find a better way to detect those cases. - let lit_snip = match snippet_opt(cx, lit.span) { + let lit_snip = match snippet_opt(cx, span) { Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip, _ => return, }; - if let LitKind::Int(value, lit_int_type) = lit.kind { + let lit_kind = LitKind::from_token_lit(lit); + if let Ok(LitKind::Int(value, lit_int_type)) = lit_kind { let suffix = match lit_int_type { LitIntType::Signed(ty) => ty.name_str(), LitIntType::Unsigned(ty) => ty.name_str(), LitIntType::Unsuffixed => "", }; - literal_suffix::check(cx, lit, &lit_snip, suffix, "integer"); + literal_suffix::check(cx, span, &lit_snip, suffix, "integer"); if lit_snip.starts_with("0x") { - mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip); + mixed_case_hex_literals::check(cx, span, suffix, &lit_snip); } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { // nothing to do } else if value != 0 && lit_snip.starts_with('0') { - zero_prefixed_literal::check(cx, lit, &lit_snip); + zero_prefixed_literal::check(cx, span, &lit_snip); } - } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { + } else if let Ok(LitKind::Float(_, LitFloatType::Suffixed(float_ty))) = lit_kind { let suffix = float_ty.name_str(); - literal_suffix::check(cx, lit, &lit_snip, suffix, "float"); + literal_suffix::check(cx, span, &lit_snip, suffix, "float"); } } } diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 9ead43ea4a47..4f9578d1b257 100644 --- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -1,20 +1,20 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast::Lit; use rustc_errors::Applicability; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::ZERO_PREFIXED_LITERAL; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) { let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0'); span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, - lit.span, + lit_span, "this is a decimal constant", |diag| { diag.span_suggestion( - lit.span, + lit_span, "if you mean to use a decimal constant, remove the `0` to avoid confusion", trimmed_lit_snip.to_string(), Applicability::MaybeIncorrect, @@ -22,7 +22,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { // do not advise to use octal form if the literal cannot be expressed in base 8. if !lit_snip.contains(|c| c == '8' || c == '9') { diag.span_suggestion( - lit.span, + lit_span, "if you mean to use an octal constant, use `0o`", format!("0o{trimmed_lit_snip}"), Applicability::MaybeIncorrect, diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index f380a5065827..2a7159764e46 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -56,11 +56,11 @@ impl EarlyLintPass for OctalEscapes { return; } - if let ExprKind::Lit(lit) = &expr.kind { - if matches!(lit.token_lit.kind, LitKind::Str) { - check_lit(cx, &lit.token_lit, lit.span, true); - } else if matches!(lit.token_lit.kind, LitKind::ByteStr) { - check_lit(cx, &lit.token_lit, lit.span, false); + if let ExprKind::Lit(token_lit) = &expr.kind { + if matches!(token_lit.kind, LitKind::Str) { + check_lit(cx, &token_lit, expr.span, true); + } else if matches!(token_lit.kind, LitKind::ByteStr) { + check_lit(cx, &token_lit, expr.span, false); } } } diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index e6e3ad05ad70..bee4a33fb4a0 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -120,7 +121,7 @@ impl EarlyLintPass for Precedence { if_chain! { if !all_odd; if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind; + if let token::LitKind::Integer | token::LitKind::Float = &lit.kind; then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 3164937293b6..3c1998d0237d 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind}; +use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -33,14 +33,14 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { if let ExprKind::MethodCall(name_ident, receiver, _, _) = &expr.kind && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") - && let ExprKind::Lit(spanned) = &receiver.kind - && let LitKind::Float(symbol, ty) = spanned.kind { - let f = symbol.as_str().parse::().unwrap(); - let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { - ty.name_str() - } else { - "" - }; + && let ExprKind::Lit(token_lit) = &receiver.kind + && token_lit.is_semantic_float() { + let f = token_lit.symbol.as_str().parse::().unwrap(); + let mut f_str = token_lit.symbol.to_string(); + match token_lit.suffix { + Some(suffix) => f_str.push_str(suffix.as_str()), + None => {} + } if f.fract() == 0.0 { Some((method_name, f_str)) } else { diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 0133997560ea..73d1ba727c82 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -152,7 +152,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { }, (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), - (Lit(l), Lit(r)) => l.kind == r.kind, + (Lit(l), Lit(r)) => l == r, (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re), (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index c5dcd7b31f58..42bdfd4827f1 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -1,4 +1,4 @@ -use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; +use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use std::iter; #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -46,10 +46,6 @@ pub struct NumericLiteral<'a> { } impl<'a> NumericLiteral<'a> { - pub fn from_lit(src: &'a str, lit: &Lit) -> Option> { - NumericLiteral::from_lit_kind(src, &lit.kind) - } - pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option> { let unsigned_src = src.strip_prefix('-').map_or(src, |s| s); if lit_kind.is_numeric() From 9f3b6e9acd367b53b80031af7e8b61226b1eaabc Mon Sep 17 00:00:00 2001 From: Kartavya Vashishtha Date: Wed, 16 Nov 2022 11:01:07 +0530 Subject: [PATCH 191/524] don't emit AlwaysBreaks if it targets a block Introduced an ignored_ids parameter. Takes O(n^2) time in the worst case. Can be changed to collect block ids in first phase, and then filter with binary search in second. --- clippy_lints/src/loops/never_loop.rs | 113 ++++++++++++++++----------- tests/ui/never_loop.rs | 11 ++- 2 files changed, 79 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index abb18187ef14..123e1e3cce15 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; +use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::Span; use std::iter::{once, Iterator}; @@ -16,7 +16,7 @@ pub(super) fn check( span: Span, for_loop: Option<&ForLoop<'_>>, ) { - match never_loop_block(block, loop_id) { + match never_loop_block(block, &mut Vec::new(), loop_id) { NeverLoopResult::AlwaysBreak => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { @@ -92,35 +92,33 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult } } -fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { - let mut iter = block +fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { + let iter = block .stmts .iter() .filter_map(stmt_to_expr) .chain(block.expr.map(|expr| (expr, None))); - never_loop_expr_seq(&mut iter, main_loop_id) -} -fn never_loop_expr_seq<'a, T: Iterator, Option<&'a Block<'a>>)>>( - es: &mut T, - main_loop_id: HirId, -) -> NeverLoopResult { - es.map(|(e, els)| { - let e = never_loop_expr(e, main_loop_id); - els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id))) + iter.map(|(e, els)| { + let e = never_loop_expr(e, ignore_ids, main_loop_id); + // els is an else block in a let...else binding + els.map_or(e, |els| { + combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id)) + }) }) .fold(NeverLoopResult::Otherwise, combine_seq) } fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { match stmt.kind { - StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)), + StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)), + // add the let...else expression (if present) StmtKind::Local(local) => local.init.map(|init| (init, local.els)), StmtKind::Item(..) => None, } } -fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { +fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { match expr.kind { ExprKind::Box(e) | ExprKind::Unary(_, e) @@ -129,48 +127,56 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Repeat(e, _) - | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id), - ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id), - ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), main_loop_id), - ExprKind::MethodCall(_, receiver, es, _) => { - never_loop_expr_all(&mut std::iter::once(receiver).chain(es.iter()), main_loop_id) - }, + | ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id), + ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id), + ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id), + ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all( + &mut std::iter::once(receiver).chain(es.iter()), + ignore_ids, + main_loop_id, + ), ExprKind::Struct(_, fields, base) => { - let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id); + let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id); if let Some(base) = base { - combine_both(fields, never_loop_expr(base, main_loop_id)) + combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id)) } else { fields } }, - ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id), + ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, main_loop_id), ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) - | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id), + | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, main_loop_id), ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. - absorb_break(never_loop_block(b, main_loop_id)) + absorb_break(never_loop_block(b, ignore_ids, main_loop_id)) }, ExprKind::If(e, e2, e3) => { - let e1 = never_loop_expr(e, main_loop_id); - let e2 = never_loop_expr(e2, main_loop_id); - let e3 = e3 - .as_ref() - .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id)); + let e1 = never_loop_expr(e, ignore_ids, main_loop_id); + let e2 = never_loop_expr(e2, ignore_ids, main_loop_id); + let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| { + never_loop_expr(e, ignore_ids, main_loop_id) + }); combine_seq(e1, combine_branches(e2, e3)) }, ExprKind::Match(e, arms, _) => { - let e = never_loop_expr(e, main_loop_id); + let e = never_loop_expr(e, ignore_ids, main_loop_id); if arms.is_empty() { e } else { - let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id); + let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id); combine_seq(e, arms) } }, - ExprKind::Block(b, None) => never_loop_block(b, main_loop_id), - ExprKind::Block(b, Some(_label)) => absorb_break(never_loop_block(b, main_loop_id)), + ExprKind::Block(b, l) => { + if let Some(_) = l { + ignore_ids.push(b.hir_id); + } + let ret = never_loop_block(b, ignore_ids, main_loop_id); + ignore_ids.pop(); + ret + }, ExprKind::Continue(d) => { let id = d .target_id @@ -181,20 +187,31 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { NeverLoopResult::AlwaysBreak } }, + ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e + .map_or(NeverLoopResult::Otherwise, |e| { + combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise) + }), ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { - combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) + combine_seq( + never_loop_expr(e, ignore_ids, main_loop_id), + NeverLoopResult::AlwaysBreak, + ) }), ExprKind::InlineAsm(asm) => asm .operands .iter() .map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { - never_loop_expr(expr, main_loop_id) + never_loop_expr(expr, ignore_ids, main_loop_id) }, - InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter().copied(), main_loop_id), - InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - never_loop_expr_all(&mut once(*in_expr).chain(out_expr.iter().copied()), main_loop_id) + InlineAsmOperand::Out { expr, .. } => { + never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id) }, + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all( + &mut once(*in_expr).chain(out_expr.iter().copied()), + ignore_ids, + main_loop_id, + ), InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, @@ -209,13 +226,21 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { } } -fn never_loop_expr_all<'a, T: Iterator>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { - es.map(|e| never_loop_expr(e, main_loop_id)) +fn never_loop_expr_all<'a, T: Iterator>>( + es: &mut T, + ignore_ids: &mut Vec, + main_loop_id: HirId, +) -> NeverLoopResult { + es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) .fold(NeverLoopResult::Otherwise, combine_both) } -fn never_loop_expr_branch<'a, T: Iterator>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult { - e.map(|e| never_loop_expr(e, main_loop_id)) +fn never_loop_expr_branch<'a, T: Iterator>>( + e: &mut T, + ignore_ids: &mut Vec, + main_loop_id: HirId, +) -> NeverLoopResult { + e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) .fold(NeverLoopResult::AlwaysBreak, combine_branches) } diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 86a5d03f765f..28e8f459d442 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -234,13 +234,22 @@ pub fn test19() { fn thing(iter: impl Iterator) { for _ in iter { 'b: { - // error goes away if we just have the block's value be (). break 'b; } } } } +pub fn test20() { + 'a: loop { + 'b: { + break 'b 'c: { + break 'a; + }; + } + } +} + fn main() { test1(); test2(); From 036a0108ac51a25ad4c51abd8182cc512b35e88a Mon Sep 17 00:00:00 2001 From: Kartavya Vashishtha Date: Wed, 16 Nov 2022 11:39:09 +0530 Subject: [PATCH 192/524] update tests --- tests/ui/never_loop.stderr | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 3033f019244a..b7029bf8bed4 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -114,5 +114,17 @@ LL | | break x; LL | | }; | |_____^ -error: aborting due to 10 previous errors +error: this loop never actually loops + --> $DIR/never_loop.rs:244:5 + | +LL | / 'a: loop { +LL | | 'b: { +LL | | break 'b 'c: { +LL | | break 'a; +LL | | }; +LL | | } +LL | | } + | |_____^ + +error: aborting due to 11 previous errors From bc3cd344a1edefdea8eef7962d75bb35cdeea880 Mon Sep 17 00:00:00 2001 From: Kartavya Vashishtha Date: Wed, 16 Nov 2022 13:15:22 +0530 Subject: [PATCH 193/524] fix clippy suggestions --- clippy_lints/src/loops/never_loop.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 123e1e3cce15..14f161f51026 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -118,6 +118,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t } } +#[allow(clippy::too_many_lines)] fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { match expr.kind { ExprKind::Box(e) @@ -170,7 +171,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: H } }, ExprKind::Block(b, l) => { - if let Some(_) = l { + if l.is_some() { ignore_ids.push(b.hir_id); } let ret = never_loop_block(b, ignore_ids, main_loop_id); @@ -187,6 +188,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: H NeverLoopResult::AlwaysBreak } }, + // checks if break targets a block instead of a loop ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e .map_or(NeverLoopResult::Otherwise, |e| { combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise) From 2bf87f383026e68bda528017906f0bee89fe34d0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 15 Nov 2022 12:06:20 +0100 Subject: [PATCH 194/524] cleanup and dedupe CTFE and Miri error reporting --- tests/ui/indexing_slicing_index.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index da5bc38b3b66..d8b6e3f1262b 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -4,11 +4,11 @@ error[E0080]: evaluation of `main::{constant#3}` failed LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -error[E0080]: erroneous constant used +note: erroneous constant used --> $DIR/indexing_slicing_index.rs:31:5 | LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. - | ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + | ^^^^^^^^^^^^^^^^^^^^^^ error: indexing may panic --> $DIR/indexing_slicing_index.rs:22:5 @@ -65,6 +65,6 @@ error[E0080]: evaluation of constant value failed LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0080`. From e5352c72c748e6f585f1b0509fa51f1acc0bc7d4 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 9 Nov 2022 10:49:28 +0000 Subject: [PATCH 195/524] Convert predicates into Predicate in the Obligation constructor --- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/ptr.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a37ee82d4c8a..218dbeaddcad 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1156,7 +1156,7 @@ fn needless_borrow_impl_arg_position<'tcx>( } let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty); - let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); let infcx = cx.tcx.infer_ctxt().build(); infcx.predicate_must_hold_modulo_regions(&obligation) }) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 642a64ae77b6..c7775313ecd0 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -419,7 +419,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< if trait_predicates.any(|predicate| { let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst); - let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) }) { return false; diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 0d74c90a834f..c8c6f32c6c98 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -695,6 +695,7 @@ fn matches_preds<'tcx>( .type_implements_trait(p.def_id, ty, p.substs, cx.param_env) .must_apply_modulo_regions(), ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new( + cx.tcx, ObligationCause::dummy(), cx.param_env, cx.tcx.mk_predicate(Binder::bind_with_vars( From 3f015a363020d3811e1f028c9ce4b0705c728289 Mon Sep 17 00:00:00 2001 From: koka Date: Thu, 17 Nov 2022 00:02:22 +0900 Subject: [PATCH 196/524] Avoid generating files via doctest When we run `cargo test` in `clippy_lints` directory, it will generate `foo.txt` in the directory. In order to avoid that, add `no_run` to rustdoc which contains `File::create`. --- clippy_lints/foo.txt | 1 - clippy_lints/src/methods/mod.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 clippy_lints/foo.txt diff --git a/clippy_lints/foo.txt b/clippy_lints/foo.txt deleted file mode 100644 index 5ab2f8a4323a..000000000000 --- a/clippy_lints/foo.txt +++ /dev/null @@ -1 +0,0 @@ -Hello \ No newline at end of file diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 43507f17e355..acb3b20a61b5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3077,7 +3077,7 @@ declare_clippy_lint! { /// /// ### Example /// - /// ```rust + /// ```rust,no_run /// use std::fs::File; /// use std::io::{self, Write, Seek, SeekFrom}; /// @@ -3090,7 +3090,7 @@ declare_clippy_lint! { /// } /// ``` /// Use instead: - /// ```rust + /// ```rust,no_run /// use std::fs::File; /// use std::io::{self, Write, Seek, SeekFrom}; /// From 333b92c5ed7c4a6849bcf72d0b026de64fde8df9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 8 Sep 2022 10:52:51 +1000 Subject: [PATCH 197/524] Box `ExprKind::{Closure,MethodCall}`, and `QSelf` in expressions, types, and patterns. --- clippy_lints/src/double_parens.rs | 8 ++--- clippy_lints/src/option_env_unwrap.rs | 6 ++-- clippy_lints/src/precedence.rs | 8 ++--- clippy_lints/src/redundant_closure_call.rs | 12 +++---- .../src/suspicious_operation_groupings.rs | 4 +-- clippy_lints/src/unnested_or_patterns.rs | 2 +- clippy_lints/src/unused_rounding.rs | 6 ++-- clippy_utils/src/ast_utils.rs | 32 ++++++++++++++++--- 8 files changed, 50 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 0f1d701865e7..29425b2e5541 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -61,10 +61,10 @@ impl EarlyLintPass for DoubleParens { } } }, - ExprKind::MethodCall(_, _, ref params, _) => { - if let [ref param] = params[..] { - if let ExprKind::Paren(_) = param.kind { - span_lint(cx, DOUBLE_PARENS, param.span, msg); + ExprKind::MethodCall(ref call) => { + if let [ref arg] = call.args[..] { + if let ExprKind::Paren(_) = arg.kind { + span_lint(cx, DOUBLE_PARENS, arg.span, msg); } } }, diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs index d9ee031c9f97..377bddeaa5fe 100644 --- a/clippy_lints/src/option_env_unwrap.rs +++ b/clippy_lints/src/option_env_unwrap.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_direct_expn_of; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind}; +use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -37,8 +37,8 @@ declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]); impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if_chain! { - if let ExprKind::MethodCall(path_segment, receiver, _, _) = &expr.kind; - if matches!(path_segment.ident.name, sym::expect | sym::unwrap); + if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap); if let ExprKind::Call(caller, _) = &receiver.kind; if is_direct_expn_of(caller.span, "option_env").is_some(); then { diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index bee4a33fb4a0..057b7e30642e 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, MethodCall, UnOp}; use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -110,11 +110,11 @@ impl EarlyLintPass for Precedence { let mut arg = operand; let mut all_odd = true; - while let ExprKind::MethodCall(path_segment, receiver, _, _) = &arg.kind { - let path_segment_str = path_segment.ident.name.as_str(); + while let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &arg.kind { + let seg_str = seg.ident.name.as_str(); all_odd &= ALLOWED_ODD_FUNCTIONS .iter() - .any(|odd_function| **odd_function == *path_segment_str); + .any(|odd_function| **odd_function == *seg_str); arg = receiver; } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 74eea6de4bbe..4cbe9597c539 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -69,10 +69,10 @@ impl EarlyLintPass for RedundantClosureCall { if_chain! { if let ast::ExprKind::Call(ref paren, _) = expr.kind; if let ast::ExprKind::Paren(ref closure) = paren.kind; - if let ast::ExprKind::Closure(_, _, ref r#async, _, ref decl, ref block, _) = closure.kind; + if let ast::ExprKind::Closure(box ast::Closure { ref asyncness, ref fn_decl, ref body, .. }) = closure.kind; then { let mut visitor = ReturnVisitor::new(); - visitor.visit_expr(block); + visitor.visit_expr(body); if !visitor.found_return { span_lint_and_then( cx, @@ -80,13 +80,13 @@ impl EarlyLintPass for RedundantClosureCall { expr.span, "try not to call a closure in the expression where it is declared", |diag| { - if decl.inputs.is_empty() { + if fn_decl.inputs.is_empty() { let app = Applicability::MachineApplicable; - let mut hint = Sugg::ast(cx, block, ".."); + let mut hint = Sugg::ast(cx, body, ".."); - if r#async.is_async() { + if asyncness.is_async() { // `async x` is a syntax error, so it becomes `async { x }` - if !matches!(block.kind, ast::ExprKind::Block(_, _)) { + if !matches!(body.kind, ast::ExprKind::Block(_, _)) { hint = hint.blockify(); } diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index eef9bdc78494..78e83880e1a6 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -580,7 +580,7 @@ fn ident_difference_expr_with_base_location( | (Await(_), Await(_)) | (Async(_, _, _), Async(_, _, _)) | (Block(_, _), Block(_, _)) - | (Closure(_, _, _, _, _, _, _), Closure(_, _, _, _, _, _, _)) + | (Closure(_), Closure(_)) | (Match(_, _), Match(_, _)) | (Loop(_, _), Loop(_, _)) | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) @@ -593,7 +593,7 @@ fn ident_difference_expr_with_base_location( | (Unary(_, _), Unary(_, _)) | (Binary(_, _, _), Binary(_, _, _)) | (Tup(_), Tup(_)) - | (MethodCall(_, _, _, _), MethodCall(_, _, _, _)) + | (MethodCall(_), MethodCall(_)) | (Call(_, _), Call(_, _)) | (ConstBlock(_), ConstBlock(_)) | (Array(_), Array(_)) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index b305dae76084..bb6fb38e9690 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -292,7 +292,7 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) /// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern /// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal. fn extend_with_struct_pat( - qself1: &Option, + qself1: &Option>, path1: &ast::Path, fps1: &mut [ast::PatField], rest1: bool, diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 3c1998d0237d..5ab351bc29ca 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::{Expr, ExprKind}; +use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -30,8 +30,8 @@ declare_clippy_lint! { declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { - if let ExprKind::MethodCall(name_ident, receiver, _, _) = &expr.kind - && let method_name = name_ident.ident.name.as_str() + if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind + && let method_name = seg.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") && let ExprKind::Lit(token_lit) = &receiver.kind && token_lit.is_semantic_float() { diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 73d1ba727c82..23aed4b5ba2f 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -75,11 +75,11 @@ pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool { && over(&l.attrs, &r.attrs, eq_attr) } -pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool { +pub fn eq_qself(l: &P, r: &P) -> bool { l.position == r.position && eq_ty(&l.ty, &r.ty) } -pub fn eq_maybe_qself(l: &Option, r: &Option) -> bool { +pub fn eq_maybe_qself(l: &Option>, r: &Option>) -> bool { match (l, r) { (Some(l), Some(r)) => eq_qself(l, r), (None, None) => true, @@ -147,8 +147,11 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)), (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), - (MethodCall(lc, ls, la, _), MethodCall(rc, rs, ra, _)) => { - eq_path_seg(lc, rc) && eq_expr(ls, rs) && over(la, ra, |l, r| eq_expr(l, r)) + ( + MethodCall(box ast::MethodCall { seg: ls, receiver: lr, args: la, .. }), + MethodCall(box ast::MethodCall { seg: rs, receiver: rr, args: ra, .. }) + ) => { + eq_path_seg(ls, rs) && eq_expr(lr, rr) && over(la, ra, |l, r| eq_expr(l, r)) }, (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), @@ -170,7 +173,26 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm), - (Closure(lb, lc, la, lm, lf, le, _), Closure(rb, rc, ra, rm, rf, re, _)) => { + ( + Closure(box ast::Closure { + binder: lb, + capture_clause: lc, + asyncness: la, + movability: lm, + fn_decl: lf, + body: le, + .. + }), + Closure(box ast::Closure { + binder: rb, + capture_clause: rc, + asyncness: ra, + movability: rm, + fn_decl: rf, + body: re, + .. + }) + ) => { eq_closure_binder(lb, rb) && lc == rc && la.is_async() == ra.is_async() From 00ae5e15a8f2fde216a09751454f5e98cf241f21 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 17 Nov 2022 14:57:39 +0000 Subject: [PATCH 198/524] Fix typo in `expect_used` and `unwrap_used` warning messages --- clippy_lints/src/methods/expect_used.rs | 6 ++--- clippy_lints/src/methods/unwrap_used.rs | 6 ++--- tests/ui-toml/expect_used/expect_used.stderr | 4 +-- tests/ui-toml/unwrap_used/unwrap_used.stderr | 26 ++++++++++---------- tests/ui/expect.stderr | 6 ++--- tests/ui/get_unwrap.stderr | 26 ++++++++++---------- tests/ui/unwrap.stderr | 6 ++--- tests/ui/unwrap_expect_used.stderr | 12 ++++----- 8 files changed, 46 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index 0d3c89280465..cce8f797e98c 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -18,9 +18,9 @@ pub(super) fn check( let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((EXPECT_USED, "an Option", "None", "")) + Some((EXPECT_USED, "an `Option`", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) + Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; @@ -36,7 +36,7 @@ pub(super) fn check( cx, lint, expr.span, - &format!("used `{method}()` on `{kind}` value"), + &format!("used `{method}()` on {kind} value"), None, &format!("if this value is {none_prefix}`{none_value}`, it will panic"), ); diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index d11830a24b6c..90983f249cd5 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -18,9 +18,9 @@ pub(super) fn check( let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((UNWRAP_USED, "an Option", "None", "")) + Some((UNWRAP_USED, "an `Option`", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) + Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; @@ -45,7 +45,7 @@ pub(super) fn check( cx, lint, expr.span, - &format!("used `unwrap{method_suffix}()` on `{kind}` value"), + &format!("used `unwrap{method_suffix}()` on {kind} value"), None, &help, ); diff --git a/tests/ui-toml/expect_used/expect_used.stderr b/tests/ui-toml/expect_used/expect_used.stderr index 28a08599c67c..1e9bb48c333c 100644 --- a/tests/ui-toml/expect_used/expect_used.stderr +++ b/tests/ui-toml/expect_used/expect_used.stderr @@ -1,4 +1,4 @@ -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/expect_used.rs:6:13 | LL | let _ = opt.expect(""); @@ -7,7 +7,7 @@ LL | let _ = opt.expect(""); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/expect_used.rs:11:13 | LL | let _ = res.expect(""); diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr index 2bca88660e1c..94b5ef663add 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); @@ -25,7 +25,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); @@ -39,7 +39,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); @@ -53,7 +53,7 @@ error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); @@ -67,7 +67,7 @@ error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); @@ -81,7 +81,7 @@ error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); @@ -95,7 +95,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); @@ -109,7 +109,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; @@ -123,7 +123,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; @@ -137,7 +137,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; @@ -151,7 +151,7 @@ error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; @@ -165,7 +165,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); @@ -179,7 +179,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); diff --git a/tests/ui/expect.stderr b/tests/ui/expect.stderr index f6738865cac1..c08e0dbbf744 100644 --- a/tests/ui/expect.stderr +++ b/tests/ui/expect.stderr @@ -1,4 +1,4 @@ -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/expect.rs:5:13 | LL | let _ = opt.expect(""); @@ -7,7 +7,7 @@ LL | let _ = opt.expect(""); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/expect.rs:10:13 | LL | let _ = res.expect(""); @@ -15,7 +15,7 @@ LL | let _ = res.expect(""); | = help: if this value is an `Err`, it will panic -error: used `expect_err()` on `a Result` value +error: used `expect_err()` on a `Result` value --> $DIR/expect.rs:11:13 | LL | let _ = res.expect_err(""); diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index 937f85904083..6dee4d5b4b62 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); @@ -25,7 +25,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); @@ -39,7 +39,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); @@ -53,7 +53,7 @@ error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); @@ -67,7 +67,7 @@ error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); @@ -81,7 +81,7 @@ error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); @@ -95,7 +95,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); @@ -109,7 +109,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; @@ -123,7 +123,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; @@ -137,7 +137,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; @@ -151,7 +151,7 @@ error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; @@ -165,7 +165,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); @@ -179,7 +179,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); diff --git a/tests/ui/unwrap.stderr b/tests/ui/unwrap.stderr index e88d580f7bd2..d49bf2b32283 100644 --- a/tests/ui/unwrap.stderr +++ b/tests/ui/unwrap.stderr @@ -1,4 +1,4 @@ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap.rs:5:13 | LL | let _ = opt.unwrap(); @@ -7,7 +7,7 @@ LL | let _ = opt.unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message = note: `-D clippy::unwrap-used` implied by `-D warnings` -error: used `unwrap()` on `a Result` value +error: used `unwrap()` on a `Result` value --> $DIR/unwrap.rs:10:13 | LL | let _ = res.unwrap(); @@ -15,7 +15,7 @@ LL | let _ = res.unwrap(); | = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message -error: used `unwrap_err()` on `a Result` value +error: used `unwrap_err()` on a `Result` value --> $DIR/unwrap.rs:11:13 | LL | let _ = res.unwrap_err(); diff --git a/tests/ui/unwrap_expect_used.stderr b/tests/ui/unwrap_expect_used.stderr index 211d2be18342..fe4ecef11453 100644 --- a/tests/ui/unwrap_expect_used.stderr +++ b/tests/ui/unwrap_expect_used.stderr @@ -1,4 +1,4 @@ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_expect_used.rs:23:5 | LL | Some(3).unwrap(); @@ -7,7 +7,7 @@ LL | Some(3).unwrap(); = help: if this value is `None`, it will panic = note: `-D clippy::unwrap-used` implied by `-D warnings` -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/unwrap_expect_used.rs:24:5 | LL | Some(3).expect("Hello world!"); @@ -16,7 +16,7 @@ LL | Some(3).expect("Hello world!"); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `unwrap()` on `a Result` value +error: used `unwrap()` on a `Result` value --> $DIR/unwrap_expect_used.rs:31:5 | LL | a.unwrap(); @@ -24,7 +24,7 @@ LL | a.unwrap(); | = help: if this value is an `Err`, it will panic -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/unwrap_expect_used.rs:32:5 | LL | a.expect("Hello world!"); @@ -32,7 +32,7 @@ LL | a.expect("Hello world!"); | = help: if this value is an `Err`, it will panic -error: used `unwrap_err()` on `a Result` value +error: used `unwrap_err()` on a `Result` value --> $DIR/unwrap_expect_used.rs:33:5 | LL | a.unwrap_err(); @@ -40,7 +40,7 @@ LL | a.unwrap_err(); | = help: if this value is an `Ok`, it will panic -error: used `expect_err()` on `a Result` value +error: used `expect_err()` on a `Result` value --> $DIR/unwrap_expect_used.rs:34:5 | LL | a.expect_err("Hello error!"); From 82afb1617938e7075021ba440f1bcc0812dc4a37 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 5 Nov 2022 15:08:37 +0100 Subject: [PATCH 199/524] Add variant_name function to `LangItem` Clippy has an internal lint that checks for the usage of hardcoded def paths and suggests to replace them with a lang or diagnostic item, if possible. This was implemented with a hack, by getting all the variants of the `LangItem` enum and then index into it with the position of the `LangItem` in the `items` list. This is no longer possible, because the `items` list can't be accessed anymore. --- .../src/utils/internal_lints/invalid_paths.rs | 12 +++++------ .../internal_lints/unnecessary_def_path.rs | 19 +++++------------- tests/ui-internal/unnecessary_def_path.fixed | 6 +++--- tests/ui-internal/unnecessary_def_path.stderr | 6 +++--- ...unnecessary_def_path_hardcoded_path.stderr | 20 +++++++++---------- 5 files changed, 27 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 25532dd4e268..22a5aa5351ad 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -79,22 +79,22 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { SimplifiedTypeGen::StrSimplifiedType, ] .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); - for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(*item_def_id); + .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied()); + for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { + let lang_item_path = cx.get_def_path(item_def_id); if path_syms.starts_with(&lang_item_path) { if let [item] = &path_syms[lang_item_path.len()..] { if matches!( - cx.tcx.def_kind(*item_def_id), + cx.tcx.def_kind(item_def_id), DefKind::Mod | DefKind::Enum | DefKind::Trait ) { - for child in cx.tcx.module_children(*item_def_id) { + for child in cx.tcx.module_children(item_def_id) { if child.ident.name == *item { return true; } } } else { - for child in cx.tcx.associated_item_def_ids(*item_def_id) { + for child in cx.tcx.associated_item_def_ids(item_def_id) { if cx.tcx.item_name(*child) == *item { return true; } diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 2a028c8141fc..cfba7fa8791d 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -6,7 +6,7 @@ use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; @@ -91,7 +91,7 @@ impl UnnecessaryDefPath { #[allow(clippy::too_many_lines)] fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { enum Item { - LangItem(Symbol), + LangItem(&'static str), DiagnosticItem(Symbol), } static PATHS: &[&[&str]] = &[ @@ -325,18 +325,9 @@ fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option, def_id: DefId) -> Option { - if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { - let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); - let item_name = cx - .tcx - .adt_def(lang_items) - .variants() - .iter() - .nth(lang_item) - .unwrap() - .name; - Some(item_name) +fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { + if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) { + Some(lang_item.variant_name()) } else { None } diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index cbbb46523064..e474f370a5d1 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -48,14 +48,14 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); - let _ = cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did); + let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did); let _ = cx.tcx.is_diagnostic_item(sym::Option, did); - let _ = cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did); + let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did); let _ = is_trait_method(cx, expr, sym::AsRef); let _ = is_path_diagnostic_item(cx, expr, sym::Option); - let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id)); + let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id)); let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); } diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr index a99a8f71fa6a..3ca29f099771 100644 --- a/tests/ui-internal/unnecessary_def_path.stderr +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -57,7 +57,7 @@ error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:51:13 | LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` error: use of a def path to a diagnostic item --> $DIR/unnecessary_def_path.rs:52:13 @@ -69,7 +69,7 @@ error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:53:13 | LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` | = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead @@ -89,7 +89,7 @@ error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:58:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:59:13 diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index af46d87bf676..2a240cc249b0 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -1,10 +1,10 @@ -error: hardcoded path to a language item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 | -LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: convert all references to use `LangItem::DerefMut` + = help: convert all references to use `sym::Deref` = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` error: hardcoded path to a diagnostic item @@ -15,13 +15,13 @@ LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", | = help: convert all references to use `sym::deref_method` -error: hardcoded path to a diagnostic item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 +error: hardcoded path to a language item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 | -LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: convert all references to use `sym::Deref` + = help: convert all references to use `LangItem::DerefMut` error: aborting due to 3 previous errors From a09423f8c8f6376a4cf4225d7fddb92ddf1f65bb Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 5 Jul 2022 16:56:16 +0000 Subject: [PATCH 200/524] Rm diagnostic item, use lang item --- clippy_lints/src/format.rs | 2 +- clippy_lints/src/format_push_string.rs | 6 ++-- clippy_lints/src/from_str_radix_10.rs | 6 ++-- clippy_lints/src/inherent_to_string.rs | 6 ++-- clippy_lints/src/manual_retain.rs | 4 +-- clippy_lints/src/manual_string_new.rs | 2 +- .../src/matches/match_str_case_mismatch.rs | 8 ++--- .../src/methods/bytes_count_to_len.rs | 5 ++-- clippy_lints/src/methods/bytes_nth.rs | 7 ++--- ...se_sensitive_file_extension_comparisons.rs | 8 ++--- clippy_lints/src/methods/expect_fun_call.rs | 6 ++-- .../src/methods/inefficient_to_string.rs | 6 ++-- clippy_lints/src/methods/manual_str_repeat.rs | 6 ++-- clippy_lints/src/methods/no_effect_replace.rs | 7 ++--- clippy_lints/src/methods/repeat_once.rs | 7 ++--- clippy_lints/src/methods/search_is_some.rs | 4 +-- .../src/methods/string_extend_chars.rs | 7 ++--- clippy_lints/src/methods/unnecessary_join.rs | 8 ++--- clippy_lints/src/needless_pass_by_value.rs | 6 ++-- clippy_lints/src/ptr.rs | 2 +- clippy_lints/src/redundant_clone.rs | 6 ++-- clippy_lints/src/strings.rs | 8 ++--- clippy_lints/src/types/box_collection.rs | 29 ++++++++++--------- clippy_lints/src/types/rc_buffer.rs | 8 ++--- .../src/unnecessary_owned_empty_strings.rs | 7 ++--- clippy_utils/src/lib.rs | 15 ++++++++-- 26 files changed, 95 insertions(+), 91 deletions(-) diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index bc0c68f535a9..d0fab6949604 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { if format_args.format_string.parts == [kw::Empty]; if arg.format.is_default(); if match cx.typeck_results().expr_ty(value).peel_refs().kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()), + ty::Adt(adt, _) => Some(adt.did()) == cx.tcx.lang_items().string(), ty::Str => true, _ => false, }; diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs index 9b9f1872bfc1..68c5c3673fe1 100644 --- a/clippy_lints/src/format_push_string.rs +++ b/clippy_lints/src/format_push_string.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{match_def_path, paths, peel_hir_expr_refs}; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -41,7 +41,7 @@ declare_clippy_lint! { declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String) + is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) } fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id { diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index cf8b7acd66d2..74a60b6a0d24 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind}; +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}; @@ -98,5 +98,5 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { /// Checks if a Ty is `String` or `&str` fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::String) || is_type_diagnostic_item(cx, ty, sym::str) + is_type_lang_item(cx, ty, LangItem::String) || is_type_diagnostic_item(cx, ty, sym::str) } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 14a37f535b46..aaecc4fa8f25 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_type_lang_item}; use clippy_utils::{return_ty, trait_ref_of_method}; use if_chain::if_chain; -use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind}; +use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { if impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })); // Check if return type is String - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::String); + if is_type_lang_item(cx, return_ty(cx, impl_item.hir_id()), LangItem::String); // Filters instances of to_string which are required by a trait if trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none(); diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 6abbab278feb..d6438ca7fec2 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq}; use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; @@ -140,7 +140,7 @@ fn check_to_owned( && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id) && match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS) && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs() - && is_type_diagnostic_item(cx, ty, sym::String) + && is_type_lang_item(cx, ty, hir::LangItem::String) && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) { suggest(cx, parent_expr, left_expr, filter_expr); } diff --git a/clippy_lints/src/manual_string_new.rs b/clippy_lints/src/manual_string_new.rs index 6acfb2ae3471..c20d7959fc4a 100644 --- a/clippy_lints/src/manual_string_new.rs +++ b/clippy_lints/src/manual_string_new.rs @@ -44,7 +44,7 @@ impl LateLintPass<'_> for ManualStringNew { let ty = cx.typeck_results().expr_ty(expr); match ty.kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => { - if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) { + if cx.tcx.lang_items().string() != Some(adt_def.did()) { return; } }, diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 6647322caa37..675a85ae5553 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::Span; use super::MATCH_STR_CASE_MISMATCH; @@ -59,7 +59,7 @@ impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); - if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str { + if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str { self.case_method = Some(case_method); return true; } diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs index fcfc25b523da..89aaad359d4a 100644 --- a/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/clippy_lints/src/methods/bytes_count_to_len.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::sym; use super::BYTES_COUNT_TO_LEN; @@ -20,7 +19,7 @@ pub(super) fn check<'tcx>( if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id); if cx.tcx.type_of(impl_id).is_str(); let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs(); - if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String); + if ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String); then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 2e96346be977..d512cc4eeae1 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; -use rustc_hir::Expr; +use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; -use rustc_span::sym; use super::BYTES_NTH; @@ -12,7 +11,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let caller_type = if ty.is_str() { "str" - } else if is_type_diagnostic_item(cx, ty, sym::String) { + } else if is_type_lang_item(cx, ty, LangItem::String) { "String" } else { return; diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index b3c2c7c9a2dc..d226c0bba659 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::{source_map::Spanned, symbol::sym, Span}; +use rustc_span::{source_map::Spanned, Span}; use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS; @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>( if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String); + if recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String); then { span_lint_and_help( cx, diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index d0cf411dfd34..a9189b31c571 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && { let arg_type = cx.typeck_results().expr_ty(receiver); let base_type = arg_type.peel_refs(); - *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::String) + *base_type.kind() == ty::Str || is_type_lang_item(cx, base_type, hir::LangItem::String) } { receiver } else { @@ -50,7 +50,7 @@ pub(super) fn check<'tcx>( // converted to string. fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { let arg_ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, arg_ty, sym::String) { + if is_type_lang_item(cx, arg_ty, hir::LangItem::String) { return false; } if let ty::Ref(_, ty, ..) = arg_ty.kind() { diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index ede3b8bb74e9..4f4f543e8a91 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use super::INEFFICIENT_TO_STRING; @@ -60,7 +60,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if is_type_diagnostic_item(cx, ty, sym::String) { + if is_type_lang_item(cx, ty, hir::LangItem::String) { return true; } diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 8b6b8f1bf16c..13c47c03a80d 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -36,14 +36,14 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { } } else { let ty = cx.typeck_results().expr_ty(e); - if is_type_diagnostic_item(cx, ty, sym::String) + if is_type_lang_item(cx, ty, LangItem::String) || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str)) || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str)) { Some(RepeatKind::String) } else { let ty = ty.peel_refs(); - (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then_some(RepeatKind::String) + (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)).then_some(RepeatKind::String) } } } @@ -58,7 +58,7 @@ pub(super) fn check( if_chain! { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String); + if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String); if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id); if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index a76341855b6d..01655e860c43 100644 --- a/clippy_lints/src/methods/no_effect_replace.rs +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::SpanlessEq; use if_chain::if_chain; use rustc_ast::LitKind; -use rustc_hir::ExprKind; +use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::sym; use super::NO_EFFECT_REPLACE; @@ -16,7 +15,7 @@ pub(super) fn check<'tcx>( arg2: &'tcx rustc_hir::Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) { + if !(ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) { return; } diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index 0a14f9216ab3..a345ec813ff5 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -1,11 +1,10 @@ use clippy_utils::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; -use rustc_hir::Expr; +use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; -use rustc_span::sym; use super::REPEAT_ONCE; @@ -37,7 +36,7 @@ pub(super) fn check<'tcx>( format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_type_diagnostic_item(cx, ty, sym::String) { + } else if is_type_lang_item(cx, ty, LangItem::String) { span_lint_and_sugg( cx, REPEAT_ONCE, diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 324c9c17b5a9..1c031ad6acba 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_trait_method, strip_pat_refs}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -105,7 +105,7 @@ pub(super) fn check<'tcx>( else if search_method == "find" { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_diagnostic_item(cx, self_ty, sym::String) { + if is_type_lang_item(cx, self_ty, hir::LangItem::String) { true } else { *self_ty.kind() == ty::Str diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 6974260f70db..6f4cec546e96 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -1,18 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::sym; use super::STRING_EXTEND_CHARS; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if !is_type_diagnostic_item(cx, obj_ty, sym::String) { + if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) { return; } if let Some(arglists) = method_chain_args(arg, &["chars"]) { @@ -20,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if *self_ty.kind() == ty::Str { "" - } else if is_type_diagnostic_item(cx, self_ty, sym::String) { + } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) { "&" } else { return; diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index 973b8a7e6bf6..c9b87bc6bf29 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -1,10 +1,10 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_middle::ty::{Ref, Slice}; -use rustc_span::{sym, Span}; +use rustc_span::Span; use super::UNNECESSARY_JOIN; @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( // the turbofish for collect is ::> if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind(); if let Slice(slice) = ref_type.kind(); - if is_type_diagnostic_item(cx, *slice, sym::String); + if is_type_lang_item(cx, *slice, LangItem::String); // the argument for join is "" if let ExprKind::Lit(spanned) = &join_arg.kind; if let LitKind::Str(symbol, _) = spanned.node; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index b2e9ce5c94d6..79aa15b06ef4 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -11,7 +11,7 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{ BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind, }; -use rustc_hir::{HirIdMap, HirIdSet}; +use rustc_hir::{HirIdMap, HirIdSet, LangItem}; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; @@ -249,7 +249,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } } - if is_type_diagnostic_item(cx, ty, sym::String) { + if is_type_lang_item(cx, ty, LangItem::String) { if let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) { diag.span_suggestion( diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 0d74c90a834f..612ee8a55a66 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -450,7 +450,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( substs.type_at(0), ), ), - Some(sym::String) => ( + _ if Some(adt.did()) == cx.tcx.lang_items().string() => ( [("clone", ".to_owned()"), ("as_str", "")].as_slice(), DerefTy::Str, ), diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index aedbe08e3e46..c1677fb3da1c 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{def_id, Body, FnDecl, HirId}; +use rustc_hir::{def_id, Body, FnDecl, HirId, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir; use rustc_middle::ty::{self, Ty}; @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD) || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD) || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD) - && is_type_diagnostic_item(cx, arg_ty, sym::String)); + && is_type_lang_item(cx, arg_ty, LangItem::String)); let from_deref = !from_borrow && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF) diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index d356c99c8fc4..f4705481d4e6 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths}; use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; @@ -190,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { }, ExprKind::Index(target, _idx) => { let e_ty = cx.typeck_results().expr_ty(target).peel_refs(); - if matches!(e_ty.kind(), ty::Str) || is_type_diagnostic_item(cx, e_ty, sym::String) { + if matches!(e_ty.kind(), ty::Str) || is_type_lang_item(cx, e_ty, LangItem::String) { span_lint( cx, STRING_SLICE, @@ -205,7 +205,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String) + is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { @@ -446,7 +446,7 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind; if path.ident.name == sym::to_string; let ty = cx.typeck_results().expr_ty(self_arg); - if is_type_diagnostic_item(cx, ty, sym::String); + if is_type_lang_item(cx, ty, LangItem::String); then { span_lint_and_help( cx, diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 08020ce66381..802415e163df 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -37,18 +37,19 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let param = qpath_generic_tys(qpath).next()?; let id = path_def_id(cx, param)?; - cx.tcx.get_diagnostic_name(id).filter(|&name| { - matches!( - name, - sym::HashMap - | sym::String - | sym::Vec - | sym::HashSet - | sym::VecDeque - | sym::LinkedList - | sym::BTreeMap - | sym::BTreeSet - | sym::BinaryHeap - ) - }) + cx.tcx + .get_diagnostic_name(id) + .filter(|&name| matches!(name, sym::HashMap | sym::Vec | sym::HashSet + | sym::VecDeque + | sym::LinkedList + | sym::BTreeMap + | sym::BTreeSet + | sym::BinaryHeap)) + .or_else(|| { + cx.tcx + .lang_items() + .string() + .filter(|did| id == *did) + .map(|_| sym::String) + }) } diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index fa567b9b2d24..855137b14d84 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -91,10 +91,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { let ty = qpath_generic_tys(qpath).next()?; let id = path_def_id(cx, ty)?; - let path = match cx.tcx.get_diagnostic_name(id)? { - sym::String => "str", - sym::OsString => "std::ffi::OsStr", - sym::PathBuf => "std::path::Path", + let path = match cx.tcx.get_diagnostic_name(id) { + Some(sym::OsString) => "std::ffi::OsStr", + Some(sym::PathBuf) => "std::path::Path", + _ if Some(id) == cx.tcx.lang_items().string() => "str", _ => return None, }; Some(path) diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index ab73f0fc44f4..9f207d32fcff 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,13 +1,12 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -61,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { if let LitKind::Str(symbol, _) = spanned.node; if symbol.is_empty(); let inner_expr_type = cx.typeck_results().expr_ty(inner_expr); - if is_type_diagnostic_item(cx, inner_expr_type, sym::String); + if is_type_lang_item(cx, inner_expr_type, LangItem::String); then { span_lint_and_sugg( cx, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d32cf1a79367..fac91dfdbda0 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -434,6 +434,16 @@ pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[ path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments)) } +/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if +/// it matches the given lang item. +pub fn is_path_lang_item<'tcx>( + cx: &LateContext<'_>, + maybe_path: &impl MaybePath<'tcx>, + lang_item: LangItem, +) -> bool { + path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id)) +} + /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given diagnostic item. pub fn is_path_diagnostic_item<'tcx>( @@ -760,7 +770,6 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - /// constructor from the std library fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool { let std_types_symbols = &[ - sym::String, sym::Vec, sym::VecDeque, sym::LinkedList, @@ -777,7 +786,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { return std_types_symbols .iter() - .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did())); + .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()); } } } @@ -834,7 +843,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & ExprKind::Lit(hir::Lit { node: LitKind::Str(ref sym, _), .. - }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String), + }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String), ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), ExprKind::Repeat(_, ArrayLen::Body(len)) => { if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind && From 921f4d317ec3498b0583a72ba1dfdfd84699c268 Mon Sep 17 00:00:00 2001 From: koka Date: Fri, 18 Nov 2022 21:23:16 +0900 Subject: [PATCH 201/524] Keep original literal notation in suggestion --- clippy_lints/src/unused_rounding.rs | 2 +- tests/ui/unused_rounding.fixed | 5 +++++ tests/ui/unused_rounding.rs | 5 +++++ tests/ui/unused_rounding.stderr | 8 +++++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 3164937293b6..0f6f36da8208 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -36,7 +36,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { && let ExprKind::Lit(spanned) = &receiver.kind && let LitKind::Float(symbol, ty) = spanned.kind { let f = symbol.as_str().parse::().unwrap(); - let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { + let f_str = spanned.token_lit.symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { ty.name_str() } else { "" diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed index 54f85806ac3b..38fe6c34cfec 100644 --- a/tests/ui/unused_rounding.fixed +++ b/tests/ui/unused_rounding.fixed @@ -6,4 +6,9 @@ fn main() { let _ = 1.0f64; let _ = 1.00f32; let _ = 2e-54f64.floor(); + + // issue9866 + let _ = 3.3_f32.round(); + let _ = 3.3_f64.round(); + let _ = 3.0_f32; } diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs index 8d007bc4a1dc..a5cac64d023a 100644 --- a/tests/ui/unused_rounding.rs +++ b/tests/ui/unused_rounding.rs @@ -6,4 +6,9 @@ fn main() { let _ = 1.0f64.floor(); let _ = 1.00f32.round(); let _ = 2e-54f64.floor(); + + // issue9866 + let _ = 3.3_f32.round(); + let _ = 3.3_f64.round(); + let _ = 3.0_f32.round(); } diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr index 6cfb02e04028..1eeb5d1de883 100644 --- a/tests/ui/unused_rounding.stderr +++ b/tests/ui/unused_rounding.stderr @@ -18,5 +18,11 @@ error: used the `round` method with a whole number float LL | let _ = 1.00f32.round(); | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32` -error: aborting due to 3 previous errors +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:13:13 + | +LL | let _ = 3.0_f32.round(); + | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32` + +error: aborting due to 4 previous errors From 928a158716b8aaee0cf1c40973b027cd63710c99 Mon Sep 17 00:00:00 2001 From: koka Date: Fri, 18 Nov 2022 21:51:43 +0900 Subject: [PATCH 202/524] Allow manual swap in const fn --- clippy_lints/src/swap.rs | 6 +++++- tests/ui/swap.fixed | 9 +++++++++ tests/ui/swap.rs | 9 +++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index f46c21e12655..68a601705516 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; @@ -138,6 +138,10 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa /// Implementation of the `MANUAL_SWAP` lint. fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { + if in_constant(cx, block.hir_id) { + return; + } + for w in block.stmts.windows(3) { if_chain! { // let t = foo(); diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index 24b229235d33..805a2ba5a598 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -155,3 +155,12 @@ fn issue_8154() { let s = S3(&mut s); std::mem::swap(&mut s.0.x, &mut s.0.y); } + +const fn issue_9864(mut u: u32) -> u32 { + let mut v = 10; + + let temp = u; + u = v; + v = temp; + u + v +} diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index a318c27919c8..a8c878479523 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -179,3 +179,12 @@ fn issue_8154() { s.0.x = s.0.y; s.0.y = t; } + +const fn issue_9864(mut u: u32) -> u32 { + let mut v = 10; + + let temp = u; + u = v; + v = temp; + u + v +} From 3c86cade4ea11baebaadb96402f8987fa013b31c Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 19 Nov 2022 00:28:02 +0900 Subject: [PATCH 203/524] Note about const fn Since `std::mem::swap` is not stable as a const fn, the suggestion would not be applicable in that cases --- clippy_lints/src/swap.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 68a601705516..c374529d1ea9 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -16,6 +16,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for manual swapping. /// + /// Note that the lint will not be emitted in const blocks, as the suggestion would not be applicable. + /// /// ### Why is this bad? /// The `std::mem::swap` function exposes the intent better /// without deinitializing or copying either variable. From 3a2eaa73f414105cbd8897381e4a3db672d98845 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 19 Nov 2022 02:22:24 +0000 Subject: [PATCH 204/524] drive-by: Add is_async fn to hir::IsAsync --- clippy_lints/src/manual_async_fn.rs | 4 ++-- clippy_lints/src/unused_async.rs | 4 ++-- clippy_utils/src/lib.rs | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 090f9f8ff73c..5c6a342b3d07 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, - HirId, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind, + HirId, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { ) { if_chain! { if let Some(header) = kind.header(); - if header.asyncness == IsAsync::NotAsync; + if !header.asyncness.is_async(); // Check that this function returns `impl Future` if let FnRetTy::Return(ret_ty) = decl.output; if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index bf487c7ca20c..3538bef6e061 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, IsAsync, YieldSource}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { span: Span, hir_id: HirId, ) { - if !span.from_expansion() && fn_kind.asyncness() == IsAsync::Async { + if !span.from_expansion() && fn_kind.asyncness().is_async() { let mut visitor = AsyncFnVisitor { cx, found_await: false }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), hir_id); if !visitor.found_await { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d32cf1a79367..bb91317d67f5 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -87,10 +87,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr, - ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, - Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, - TraitRef, TyKind, UnOp, + def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, + Destination, Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, + LangItem, Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, + QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -1861,7 +1861,7 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { - matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async) + matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness.is_async()) } /// Peels away all the compiler generated code surrounding the body of an async function, From a09143866f7359826a4b7cd7125d1a7c4cff981f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 19 Nov 2022 03:28:56 +0000 Subject: [PATCH 205/524] drive-by: PolyExistentialPredicate --- clippy_lints/src/ptr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index c8c6f32c6c98..180d534636e0 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -687,7 +687,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: fn matches_preds<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, - preds: &'tcx [Binder<'tcx, ExistentialPredicate<'tcx>>], + preds: &'tcx [ty::PolyExistentialPredicate<'tcx>], ) -> bool { let infcx = cx.tcx.infer_ctxt().build(); preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) { From ef5f60285faa6444e293214bd0137511f4e40d0d Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 18 Nov 2022 18:18:58 +0000 Subject: [PATCH 206/524] Move `line_span` to source.rs --- clippy_utils/src/lib.rs | 20 +------------------- clippy_utils/src/source.rs | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 670b740d8418..b0063758dcef 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -108,11 +108,10 @@ use rustc_middle::ty::{FloatTy, IntTy, UintTy}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::source_map::original_sp; use rustc_span::source_map::SourceMap; use rustc_span::sym; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; @@ -1302,23 +1301,6 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { .is_some() } -/// Extends the span to the beginning of the spans line, incl. whitespaces. -/// -/// ```rust -/// let x = (); -/// // ^^ -/// // will be converted to -/// let x = (); -/// // ^^^^^^^^^^^^^^ -/// ``` -fn line_span(cx: &T, span: Span) -> Span { - let span = original_sp(span, DUMMY_SP); - let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); - let line_no = source_map_and_line.line; - let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]); - span.with_lo(line_start) -} - /// Gets the parent node, if any. pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { tcx.hir().parent_iter(id).next().map(|(_, node)| node) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index d28bd92d708b..eacfa91ba556 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -2,13 +2,12 @@ #![allow(clippy::module_name_repetitions)] -use crate::line_span; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::hygiene; -use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext}; +use rustc_span::source_map::{original_sp, SourceMap}; +use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; use std::borrow::Cow; /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. @@ -55,6 +54,23 @@ fn first_char_in_first_line(cx: &T, span: Span) -> Option(cx: &T, span: Span) -> Span { + let span = original_sp(span, DUMMY_SP); + let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); + let line_no = source_map_and_line.line; + let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]); + span.with_lo(line_start) +} + /// Returns the indentation of the line of a span /// /// ```rust,ignore From 98b343c5e6a5059a420d0b2d38edad0b719520e8 Mon Sep 17 00:00:00 2001 From: Caio Date: Sat, 19 Nov 2022 08:22:27 -0300 Subject: [PATCH 207/524] [arithmetic-side-effects] Detect overflowing associated constants of integers --- .../src/operators/arithmetic_side_effects.rs | 115 +++++++++--------- .../arithmetic_side_effects_allowed.rs | 11 +- tests/ui/arithmetic_side_effects.rs | 8 +- tests/ui/arithmetic_side_effects.stderr | 110 ++++++++--------- 4 files changed, 130 insertions(+), 114 deletions(-) diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 8827daaa3ee7..eda65e301535 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,12 +1,19 @@ use super::ARITHMETIC_SIDE_EFFECTS; -use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; +use clippy_utils::{ + consts::{constant, constant_simple}, + diagnostics::span_lint, + peel_hir_expr_refs, +}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; -use rustc_span::source_map::{Span, Spanned}; +use rustc_span::{ + source_map::{Span, Spanned}, + sym, +}; const HARD_CODED_ALLOWED: &[&str] = &[ "&str", @@ -38,24 +45,6 @@ impl ArithmeticSideEffects { } } - /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a - /// non-constant environment that won't overflow. - fn has_valid_op(op: &Spanned, expr: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::Lit(ref lit) = expr.kind && - let ast::LitKind::Int(value, _) = lit.node - { - match (&op.node, value) { - (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, - (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) - | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) - | (hir::BinOpKind::Mul, 0 | 1) => true, - _ => false, - } - } else { - false - } - } - /// Checks if the given `expr` has any of the inner `allowed` elements. fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { self.allowed @@ -74,15 +63,14 @@ impl ArithmeticSideEffects { self.expr_span = Some(expr.span); } - /// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`. - fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option> { - if matches!(expr.kind, hir::ExprKind::Lit(_)) { - return Some(LiteralIntegerTy::Value(expr)); + /// If `expr` is not a literal integer like `1`, returns `None`. + fn literal_integer(expr: &hir::Expr<'_>) -> Option { + if let hir::ExprKind::Lit(ref lit) = expr.kind && let ast::LitKind::Int(n, _) = lit.node { + Some(n) } - if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind { - return Some(LiteralIntegerTy::Ref(inn)); + else { + None } - None } /// Manages when the lint should be triggered. Operations in constant environments, hard coded @@ -117,10 +105,20 @@ impl ArithmeticSideEffects { return; } let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { - match (Self::literal_integer(lhs), Self::literal_integer(rhs)) { - (None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()), - (Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true, - (None, None) | (Some(_), Some(_)) => false, + let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); + let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); + match (Self::literal_integer(actual_lhs), Self::literal_integer(actual_rhs)) { + (None, None) => false, + (None, Some(n)) | (Some(n), None) => match (&op.node, n) { + (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) + | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) + | (hir::BinOpKind::Mul, 0 | 1) => true, + _ => false, + }, + (Some(_), Some(_)) => { + matches!((lhs_ref_counter, rhs_ref_counter), (0, 0)) + }, } } else { false @@ -129,21 +127,45 @@ impl ArithmeticSideEffects { self.issue_lint(cx, expr); } } + + fn manage_unary_ops<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + expr: &hir::Expr<'tcx>, + un_expr: &hir::Expr<'tcx>, + un_op: hir::UnOp, + ) { + let hir::UnOp::Neg = un_op else { return; }; + if constant(cx, cx.typeck_results(), un_expr).is_some() { + return; + } + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + if self.is_allowed_ty(ty) { + return; + } + let actual_un_expr = peel_hir_expr_refs(un_expr).0; + if Self::literal_integer(actual_un_expr).is_some() { + return; + } + self.issue_lint(cx, expr); + } + + fn should_skip_expr(&mut self, expr: &hir::Expr<'_>) -> bool { + self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) + } } impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { - if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) { + if self.should_skip_expr(expr) { return; } match &expr.kind { - hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => { + hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => { self.manage_bin_ops(cx, expr, op, lhs, rhs); }, - hir::ExprKind::Unary(hir::UnOp::Neg, _) => { - if constant_simple(cx, cx.typeck_results(), expr).is_none() { - self.issue_lint(cx, expr); - } + hir::ExprKind::Unary(un_op, un_expr) => { + self.manage_unary_ops(cx, expr, un_expr, *un_op); }, _ => {}, } @@ -177,22 +199,3 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { } } } - -/// Tells if an expression is a integer declared by value or by reference. -/// -/// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather -/// than `hirExprKind::Addr`. -enum LiteralIntegerTy<'expr, 'tcx> { - /// For example, `&199` - Ref(&'expr hir::Expr<'tcx>), - /// For example, `1` or `i32::MAX` - Value(&'expr hir::Expr<'tcx>), -} - -impl<'expr, 'tcx> From> for &'expr hir::Expr<'tcx> { - fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self { - match from { - LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem, - } - } -} diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs index 1aed09b7c7bd..e8a023ab1764 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs @@ -1,6 +1,6 @@ #![warn(clippy::arithmetic_side_effects)] -use core::ops::Add; +use core::ops::{Add, Neg}; #[derive(Clone, Copy)] struct Point { @@ -16,9 +16,18 @@ impl Add for Point { } } +impl Neg for Point { + type Output = Self; + + fn neg(self) -> Self::Output { + todo!() + } +} + fn main() { let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 }; let point: Point = Point { x: 1, y: 0 }; let _ = point + point; + let _ = -point; } diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index b25e68f13061..b5ed8988a518 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -150,8 +150,12 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n = 23 + 85; // Unary - _n = -1; - _n = -(-1); + _n = -2147483647; + _n = -i32::MAX; + _n = -i32::MIN; + _n = -&2147483647; + _n = -&i32::MAX; + _n = -&i32::MIN; } pub fn runtime_ops() { diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 0f06e22bae96..0259a0824e79 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -19,331 +19,331 @@ LL | let _ = inferred_string + ""; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:161:5 + --> $DIR/arithmetic_side_effects.rs:165:5 | LL | _n += 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:162:5 + --> $DIR/arithmetic_side_effects.rs:166:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:163:5 + --> $DIR/arithmetic_side_effects.rs:167:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:164:5 + --> $DIR/arithmetic_side_effects.rs:168:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:165:5 + --> $DIR/arithmetic_side_effects.rs:169:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:166:5 + --> $DIR/arithmetic_side_effects.rs:170:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:167:5 + --> $DIR/arithmetic_side_effects.rs:171:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:168:5 + --> $DIR/arithmetic_side_effects.rs:172:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:169:5 + --> $DIR/arithmetic_side_effects.rs:173:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:170:5 + --> $DIR/arithmetic_side_effects.rs:174:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:173:10 + --> $DIR/arithmetic_side_effects.rs:177:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:174:10 + --> $DIR/arithmetic_side_effects.rs:178:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:175:10 + --> $DIR/arithmetic_side_effects.rs:179:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:176:10 + --> $DIR/arithmetic_side_effects.rs:180:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:177:10 + --> $DIR/arithmetic_side_effects.rs:181:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:178:10 + --> $DIR/arithmetic_side_effects.rs:182:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:179:10 + --> $DIR/arithmetic_side_effects.rs:183:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:180:10 + --> $DIR/arithmetic_side_effects.rs:184:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:181:10 + --> $DIR/arithmetic_side_effects.rs:185:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:182:10 + --> $DIR/arithmetic_side_effects.rs:186:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:183:10 + --> $DIR/arithmetic_side_effects.rs:187:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:184:10 + --> $DIR/arithmetic_side_effects.rs:188:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:185:10 + --> $DIR/arithmetic_side_effects.rs:189:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:186:10 + --> $DIR/arithmetic_side_effects.rs:190:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:187:10 + --> $DIR/arithmetic_side_effects.rs:191:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:188:10 + --> $DIR/arithmetic_side_effects.rs:192:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:189:10 + --> $DIR/arithmetic_side_effects.rs:193:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:190:10 + --> $DIR/arithmetic_side_effects.rs:194:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:191:10 + --> $DIR/arithmetic_side_effects.rs:195:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:194:13 + --> $DIR/arithmetic_side_effects.rs:198:13 | LL | let _ = Custom + 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:195:13 + --> $DIR/arithmetic_side_effects.rs:199:13 | LL | let _ = Custom + 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:196:13 + --> $DIR/arithmetic_side_effects.rs:200:13 | LL | let _ = Custom + 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:197:13 + --> $DIR/arithmetic_side_effects.rs:201:13 | LL | let _ = Custom + 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:198:13 + --> $DIR/arithmetic_side_effects.rs:202:13 | LL | let _ = Custom + 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:199:13 + --> $DIR/arithmetic_side_effects.rs:203:13 | LL | let _ = Custom + 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:200:13 + --> $DIR/arithmetic_side_effects.rs:204:13 | LL | let _ = Custom - 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:201:13 + --> $DIR/arithmetic_side_effects.rs:205:13 | LL | let _ = Custom - 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:202:13 + --> $DIR/arithmetic_side_effects.rs:206:13 | LL | let _ = Custom - 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:203:13 + --> $DIR/arithmetic_side_effects.rs:207:13 | LL | let _ = Custom - 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:204:13 + --> $DIR/arithmetic_side_effects.rs:208:13 | LL | let _ = Custom - 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:205:13 + --> $DIR/arithmetic_side_effects.rs:209:13 | LL | let _ = Custom - 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:206:13 + --> $DIR/arithmetic_side_effects.rs:210:13 | LL | let _ = Custom / 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:207:13 + --> $DIR/arithmetic_side_effects.rs:211:13 | LL | let _ = Custom / 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:208:13 + --> $DIR/arithmetic_side_effects.rs:212:13 | LL | let _ = Custom / 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:209:13 + --> $DIR/arithmetic_side_effects.rs:213:13 | LL | let _ = Custom / 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:210:13 + --> $DIR/arithmetic_side_effects.rs:214:13 | LL | let _ = Custom / 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:211:13 + --> $DIR/arithmetic_side_effects.rs:215:13 | LL | let _ = Custom / 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:212:13 + --> $DIR/arithmetic_side_effects.rs:216:13 | LL | let _ = Custom * 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:213:13 + --> $DIR/arithmetic_side_effects.rs:217:13 | LL | let _ = Custom * 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:214:13 + --> $DIR/arithmetic_side_effects.rs:218:13 | LL | let _ = Custom * 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:215:13 + --> $DIR/arithmetic_side_effects.rs:219:13 | LL | let _ = Custom * 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:216:13 + --> $DIR/arithmetic_side_effects.rs:220:13 | LL | let _ = Custom * 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:217:13 + --> $DIR/arithmetic_side_effects.rs:221:13 | LL | let _ = Custom * 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:220:10 + --> $DIR/arithmetic_side_effects.rs:224:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:221:10 + --> $DIR/arithmetic_side_effects.rs:225:10 | LL | _n = -&_n; | ^^^^ From e0b1463e0a0133ae49fc31aab3e7f18882ff00b0 Mon Sep 17 00:00:00 2001 From: Caio Date: Sat, 19 Nov 2022 08:25:20 -0300 Subject: [PATCH 208/524] Remove unused --- clippy_lints/src/operators/arithmetic_side_effects.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index eda65e301535..20b82d81a2ae 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -10,10 +10,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; -use rustc_span::{ - source_map::{Span, Spanned}, - sym, -}; +use rustc_span::source_map::{Span, Spanned}; const HARD_CODED_ALLOWED: &[&str] = &[ "&str", From 1baa6cd591982471475895fc3256d5162a651628 Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 19 Nov 2022 21:21:47 +0900 Subject: [PATCH 209/524] refac: grab a snip from receiver --- clippy_lints/src/unused_rounding.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 0f6f36da8208..cf34a57f91d1 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind}; +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet}; +use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -29,19 +29,15 @@ declare_clippy_lint! { } declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); -fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { +fn is_useless_rounding<'a>(cx: &EarlyContext<'a>, expr: &'a Expr) -> Option<(&'a str, String)> { if let ExprKind::MethodCall(name_ident, receiver, _, _) = &expr.kind && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") && let ExprKind::Lit(spanned) = &receiver.kind - && let LitKind::Float(symbol, ty) = spanned.kind { + && let LitKind::Float(symbol, _) = spanned.kind { let f = symbol.as_str().parse::().unwrap(); - let f_str = spanned.token_lit.symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { - ty.name_str() - } else { - "" - }; if f.fract() == 0.0 { + let f_str = snippet(cx, receiver.span, "..").to_string(); Some((method_name, f_str)) } else { None @@ -53,7 +49,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { impl EarlyLintPass for UnusedRounding { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if let Some((method_name, float)) = is_useless_rounding(expr) { + if let Some((method_name, float)) = is_useless_rounding(cx, expr) { span_lint_and_sugg( cx, UNUSED_ROUNDING, From 4d8af99365d73725b4d46bfc4cd993d6c3af63c5 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 19 Nov 2022 18:13:17 +0000 Subject: [PATCH 210/524] Fix `#[allow]` for module_name_repetitions & single_component_path_imports --- clippy_lints/src/attrs.rs | 4 +- clippy_lints/src/lib.rs | 2 +- .../src/single_component_path_imports.rs | 219 ++++++++++-------- tests/ui/single_component_path_imports.stderr | 12 +- ...component_path_imports_nested_first.stderr | 15 +- tests/ui/useless_attribute.fixed | 21 +- tests/ui/useless_attribute.rs | 21 +- tests/ui/useless_attribute.stderr | 6 +- 8 files changed, 180 insertions(+), 120 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 235ab4977ce4..ecf8e83375db 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -378,7 +378,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { | "enum_glob_use" | "redundant_pub_crate" | "macro_use_imports" - | "unsafe_removed_from_name", + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" ) }) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4e640d7ebf65..3ab5031696d5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -792,7 +792,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); store.register_early_pass(|| Box::new(as_conversions::AsConversions)); store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); - store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports)); + store.register_early_pass(|| Box::::default()); let max_fn_params_bools = conf.max_fn_params_bools; let max_struct_bools = conf.max_struct_bools; store.register_late_pass(move |_| { diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 66b79513032f..2036e85db7e8 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; declare_clippy_lint! { @@ -33,51 +34,32 @@ declare_clippy_lint! { "imports with single component path are redundant" } -declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); +#[derive(Default)] +pub struct SingleComponentPathImports { + /// Buffer found usages to emit when visiting that item so that `#[allow]` works as expected + found: NodeMap>, +} + +struct SingleUse { + name: Symbol, + span: Span, + item_id: NodeId, + can_suggest: bool, +} + +impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { if cx.sess().opts.edition < Edition::Edition2018 { return; } - check_mod(cx, &krate.items); - } -} -fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { - // keep track of imports reused with `self` keyword, - // such as `self::crypto_hash` in the example below - // ```rust,ignore - // use self::crypto_hash::{Algorithm, Hasher}; - // ``` - let mut imports_reused_with_self = Vec::new(); - - // keep track of single use statements - // such as `crypto_hash` in the example below - // ```rust,ignore - // use crypto_hash; - // ``` - let mut single_use_usages = Vec::new(); - - // keep track of macros defined in the module as we don't want it to trigger on this (#7106) - // ```rust,ignore - // macro_rules! foo { () => {} }; - // pub(crate) use foo; - // ``` - let mut macros = Vec::new(); - - for item in items { - track_uses( - cx, - item, - &mut imports_reused_with_self, - &mut single_use_usages, - &mut macros, - ); + self.check_mod(cx, &krate.items); } - for (name, span, can_suggest) in single_use_usages { - if !imports_reused_with_self.contains(&name) { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + for SingleUse { span, can_suggest, .. } in self.found.remove(&item.id).into_iter().flatten() { if can_suggest { span_lint_and_sugg( cx, @@ -102,74 +84,127 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { } } -fn track_uses( - cx: &EarlyContext<'_>, - item: &Item, - imports_reused_with_self: &mut Vec, - single_use_usages: &mut Vec<(Symbol, Span, bool)>, - macros: &mut Vec, -) { - if item.span.from_expansion() || item.vis.kind.is_pub() { - return; - } +impl SingleComponentPathImports { + fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P]) { + // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example + // below. Removing the `use crypto_hash;` would make this a compile error + // ``` + // use crypto_hash; + // + // use self::crypto_hash::{Algorithm, Hasher}; + // ``` + let mut imports_reused_with_self = Vec::new(); - match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { - check_mod(cx, items); - }, - ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { - macros.push(item.ident.name); - }, - ItemKind::Use(use_tree) => { - let segments = &use_tree.prefix.segments; - - // keep track of `use some_module;` usages - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = use_tree.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push((name, item.span, true)); - } - } - return; + // keep track of single use statements such as `crypto_hash` in the example below + // ``` + // use crypto_hash; + // ``` + let mut single_use_usages = Vec::new(); + + // keep track of macros defined in the module as we don't want it to trigger on this (#7106) + // ``` + // macro_rules! foo { () => {} }; + // pub(crate) use foo; + // ``` + let mut macros = Vec::new(); + + for item in items { + self.track_uses( + cx, + item, + &mut imports_reused_with_self, + &mut single_use_usages, + &mut macros, + ); + } + + for usage in single_use_usages { + if !imports_reused_with_self.contains(&usage.name) { + self.found.entry(usage.item_id).or_default().push(usage); } + } + } - if segments.is_empty() { - // keep track of `use {some_module, some_other_module};` usages - if let UseTreeKind::Nested(trees) = &use_tree.kind { - for tree in trees { - let segments = &tree.0.prefix.segments; - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = tree.0.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push((name, tree.0.span, false)); - } - } + fn track_uses( + &mut self, + cx: &EarlyContext<'_>, + item: &Item, + imports_reused_with_self: &mut Vec, + single_use_usages: &mut Vec, + macros: &mut Vec, + ) { + if item.span.from_expansion() || item.vis.kind.is_pub() { + return; + } + + match &item.kind { + ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + self.check_mod(cx, items); + }, + ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { + macros.push(item.ident.name); + }, + ItemKind::Use(use_tree) => { + let segments = &use_tree.prefix.segments; + + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: item.span, + item_id: item.id, + can_suggest: true, + }); } } + return; } - } else { - // keep track of `use self::some_module` usages - if segments[0].ident.name == kw::SelfLower { - // simple case such as `use self::module::SomeStruct` - if segments.len() > 1 { - imports_reused_with_self.push(segments[1].ident.name); - return; - } - // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if segments.is_empty() { + // keep track of `use {some_module, some_other_module};` usages if let UseTreeKind::Nested(trees) = &use_tree.kind { for tree in trees { let segments = &tree.0.prefix.segments; - if !segments.is_empty() { - imports_reused_with_self.push(segments[0].ident.name); + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = tree.0.kind { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: tree.0.span, + item_id: item.id, + can_suggest: false, + }); + } + } + } + } + } + } else { + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + imports_reused_with_self.push(segments[1].ident.name); + return; + } + + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + imports_reused_with_self.push(segments[0].ident.name); + } } } } } - } - }, - _ => {}, + }, + _ => {}, + } } } diff --git a/tests/ui/single_component_path_imports.stderr b/tests/ui/single_component_path_imports.stderr index 509c88ac256a..71dcc25d6e5b 100644 --- a/tests/ui/single_component_path_imports.stderr +++ b/tests/ui/single_component_path_imports.stderr @@ -1,16 +1,16 @@ error: this import is redundant - --> $DIR/single_component_path_imports.rs:23:5 + --> $DIR/single_component_path_imports.rs:5:1 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely | = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant - --> $DIR/single_component_path_imports.rs:5:1 + --> $DIR/single_component_path_imports.rs:23:5 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely error: aborting due to 2 previous errors diff --git a/tests/ui/single_component_path_imports_nested_first.stderr b/tests/ui/single_component_path_imports_nested_first.stderr index 633546f6419a..330f285202d0 100644 --- a/tests/ui/single_component_path_imports_nested_first.stderr +++ b/tests/ui/single_component_path_imports_nested_first.stderr @@ -1,3 +1,11 @@ +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:4:1 + | +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely + | + = note: `-D clippy::single-component-path-imports` implied by `-D warnings` + error: this import is redundant --> $DIR/single_component_path_imports_nested_first.rs:13:10 | @@ -5,7 +13,6 @@ LL | use {regex, serde}; | ^^^^^ | = help: remove this import - = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant --> $DIR/single_component_path_imports_nested_first.rs:13:17 @@ -15,11 +22,5 @@ LL | use {regex, serde}; | = help: remove this import -error: this import is redundant - --> $DIR/single_component_path_imports_nested_first.rs:4:1 - | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely - error: aborting due to 3 previous errors diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index c23231a99e9f..871e4fb5c3a9 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![allow(unused)] #![warn(clippy::useless_attribute)] #![warn(unreachable_pub)] #![feature(rustc_private)] @@ -16,6 +17,13 @@ extern crate rustc_middle; #[macro_use] extern crate proc_macro_derive; +fn test_indented_attr() { + #![allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::::default(); +} + // don't lint on unused_import for `use` items #[allow(unused_imports)] use std::collections; @@ -63,13 +71,16 @@ mod c { pub(crate) struct S; } -fn test_indented_attr() { - #![allow(clippy::almost_swapped)] - use std::collections::HashSet; - - let _ = HashSet::::default(); +// https://github.com/rust-lang/rust-clippy/issues/7511 +pub mod split { + #[allow(clippy::module_name_repetitions)] + pub use regex::SplitN; } +// https://github.com/rust-lang/rust-clippy/issues/8768 +#[allow(clippy::single_component_path_imports)] +use regex; + fn main() { test_indented_attr(); } diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 7a7b198ea607..cb50736ba395 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![allow(unused)] #![warn(clippy::useless_attribute)] #![warn(unreachable_pub)] #![feature(rustc_private)] @@ -16,6 +17,13 @@ extern crate rustc_middle; #[macro_use] extern crate proc_macro_derive; +fn test_indented_attr() { + #[allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::::default(); +} + // don't lint on unused_import for `use` items #[allow(unused_imports)] use std::collections; @@ -63,13 +71,16 @@ mod c { pub(crate) struct S; } -fn test_indented_attr() { - #[allow(clippy::almost_swapped)] - use std::collections::HashSet; - - let _ = HashSet::::default(); +// https://github.com/rust-lang/rust-clippy/issues/7511 +pub mod split { + #[allow(clippy::module_name_repetitions)] + pub use regex::SplitN; } +// https://github.com/rust-lang/rust-clippy/issues/8768 +#[allow(clippy::single_component_path_imports)] +use regex; + fn main() { test_indented_attr(); } diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index 255d28763553..a7ea0df22945 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -1,5 +1,5 @@ error: useless lint attribute - --> $DIR/useless_attribute.rs:8:1 + --> $DIR/useless_attribute.rs:9:1 | LL | #[allow(dead_code)] | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]` @@ -7,13 +7,13 @@ LL | #[allow(dead_code)] = note: `-D clippy::useless-attribute` implied by `-D warnings` error: useless lint attribute - --> $DIR/useless_attribute.rs:9:1 + --> $DIR/useless_attribute.rs:10:1 | LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:67:5 + --> $DIR/useless_attribute.rs:21:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` From 31b83d0895d37dc8a37e195f75bb9fe7de2c5e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Tue, 1 Nov 2022 18:39:36 +0100 Subject: [PATCH 211/524] Add missnamed_getters lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/functions/missnamed_getters.rs | 123 ++++++++++++++++++ clippy_lints/src/functions/mod.rs | 22 ++++ tests/ui/missnamed_getters.rs | 28 ++++ tests/ui/missnamed_getters.stderr | 16 +++ 6 files changed, 191 insertions(+) create mode 100644 clippy_lints/src/functions/missnamed_getters.rs create mode 100644 tests/ui/missnamed_getters.rs create mode 100644 tests/ui/missnamed_getters.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f1f73c1fd2f..180e7d8bedcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4198,6 +4198,7 @@ Released 2018-09-13 [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop [`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods +[`missnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#missnamed_getters [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals [`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 0d3fc43a6443..0c9ae6380d87 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -184,6 +184,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::functions::RESULT_UNIT_ERR_INFO, crate::functions::TOO_MANY_ARGUMENTS_INFO, crate::functions::TOO_MANY_LINES_INFO, + crate::functions::MISSNAMED_GETTERS_INFO, crate::future_not_send::FUTURE_NOT_SEND_INFO, crate::if_let_mutex::IF_LET_MUTEX_INFO, crate::if_not_else::IF_NOT_ELSE_INFO, diff --git a/clippy_lints/src/functions/missnamed_getters.rs b/clippy_lints/src/functions/missnamed_getters.rs new file mode 100644 index 000000000000..c522bb780b3d --- /dev/null +++ b/clippy_lints/src/functions/missnamed_getters.rs @@ -0,0 +1,123 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::Span; + +use super::MISSNAMED_GETTERS; + +pub fn check_fn( + cx: &LateContext<'_>, + kind: FnKind<'_>, + decl: &FnDecl<'_>, + body: &Body<'_>, + _span: Span, + _hir_id: HirId, +) { + let FnKind::Method(ref ident, sig) = kind else { + return; + }; + + // Takes only &(mut) self + if decl.inputs.len() != 1 { + return; + } + + let name = ident.name.as_str(); + + let name = match sig.decl.implicit_self { + ImplicitSelfKind::ImmRef => name, + ImplicitSelfKind::MutRef => { + let Some(name) = name.strip_suffix("_mut") else { + return; + }; + name + }, + _ => return, + }; + + // Body must be &(mut) .name + // self_data is not neccessarilly self + let (self_data, used_ident, span) = if_chain! { + if let ExprKind::Block(block,_) = body.value.kind; + if block.stmts.is_empty(); + if let Some(block_expr) = block.expr; + // replace with while for as many addrof needed + if let ExprKind::AddrOf(_,_, expr) = block_expr.kind; + if let ExprKind::Field(self_data, ident) = expr.kind; + if ident.name.as_str() != name; + then { + (self_data,ident,block_expr.span) + } else { + return; + } + }; + + let ty = cx.typeck_results().expr_ty(self_data); + + let def = { + let mut kind = ty.kind(); + loop { + match kind { + ty::Adt(def, _) => break def, + ty::Ref(_, ty, _) => kind = ty.kind(), + // We don't do tuples because the function name cannot be a number + _ => return, + } + } + }; + + let variants = def.variants(); + + // We're accessing a field, so it should be an union or a struct and have one and only one variant + if variants.len() != 1 { + if cfg!(debug_assertions) { + panic!("Struct or union expected to have only one variant"); + } else { + // Don't ICE when possible + return; + } + } + + let first = variants.last().unwrap(); + let fields = &variants[first]; + + let mut used_field = None; + let mut correct_field = None; + for f in &fields.fields { + if f.name.as_str() == name { + correct_field = Some(f); + } + if f.name == used_ident.name { + used_field = Some(f); + } + } + + let Some(used_field) = used_field else { + if cfg!(debug_assertions) { + panic!("Struct doesn't contain the correct field"); + } else { + // Don't ICE when possible + return; + } + }; + let Some(correct_field) = correct_field else { + return; + }; + + if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) { + let snippet = snippet(cx, span, ".."); + let sugg = format!("{}{name}", snippet.strip_suffix(used_field.name.as_str()).unwrap()); + span_lint_and_sugg( + cx, + MISSNAMED_GETTERS, + span, + "getter function appears to return the wrong field", + "consider using", + sugg, + Applicability::MaybeIncorrect, + ); + } +} diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index ae0e08334463..726df02444fc 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,3 +1,4 @@ +mod missnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; mod result; @@ -260,6 +261,25 @@ declare_clippy_lint! { "function returning `Result` with large `Err` type" } +declare_clippy_lint! { + /// ### What it does + /// + /// ### Why is this bad? + /// + /// ### Example + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + #[clippy::version = "1.66.0"] + pub MISSNAMED_GETTERS, + suspicious, + "default lint description" +} + #[derive(Copy, Clone)] pub struct Functions { too_many_arguments_threshold: u64, @@ -286,6 +306,7 @@ impl_lint_pass!(Functions => [ MUST_USE_CANDIDATE, RESULT_UNIT_ERR, RESULT_LARGE_ERR, + MISSNAMED_GETTERS, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -301,6 +322,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); + missnamed_getters::check_fn(cx, kind, decl, body, span, hir_id); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { diff --git a/tests/ui/missnamed_getters.rs b/tests/ui/missnamed_getters.rs new file mode 100644 index 000000000000..b47f6edc5ba1 --- /dev/null +++ b/tests/ui/missnamed_getters.rs @@ -0,0 +1,28 @@ +#![allow(unused)] +#![warn(clippy::missnamed_getters)] + +struct A { + a: u8, + b: u8, +} + +impl A { + fn a(&self) -> &u8 { + &self.b + } +} + +union B { + a: u8, + b: u8, +} + +impl B { + unsafe fn a(&self) -> &u8 { + &self.b + } +} + +fn main() { + // test code goes here +} diff --git a/tests/ui/missnamed_getters.stderr b/tests/ui/missnamed_getters.stderr new file mode 100644 index 000000000000..8e31a42b97c1 --- /dev/null +++ b/tests/ui/missnamed_getters.stderr @@ -0,0 +1,16 @@ +error: getter function appears to return the wrong field + --> $DIR/missnamed_getters.rs:11:9 + | +LL | &self.b + | ^^^^^^^ help: consider using: `&self.a` + | + = note: `-D clippy::missnamed-getters` implied by `-D warnings` + +error: getter function appears to return the wrong field + --> $DIR/missnamed_getters.rs:22:9 + | +LL | &self.b + | ^^^^^^^ help: consider using: `&self.a` + +error: aborting due to 2 previous errors + From 9891af348c01f8cf14ab1e10754faccad04fcaa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Tue, 1 Nov 2022 19:07:51 +0100 Subject: [PATCH 212/524] missnamed_getters: Match owned methods --- .../src/functions/missnamed_getters.rs | 27 +++++++--- tests/ui/missnamed_getters.rs | 39 ++++++++++++++ tests/ui/missnamed_getters.stderr | 54 +++++++++++++++++-- 3 files changed, 110 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/functions/missnamed_getters.rs b/clippy_lints/src/functions/missnamed_getters.rs index c522bb780b3d..1f4eefc620bd 100644 --- a/clippy_lints/src/functions/missnamed_getters.rs +++ b/clippy_lints/src/functions/missnamed_getters.rs @@ -35,21 +35,34 @@ pub fn check_fn( }; name }, + ImplicitSelfKind::Imm | ImplicitSelfKind::Mut => name, _ => return, }; // Body must be &(mut) .name - // self_data is not neccessarilly self - let (self_data, used_ident, span) = if_chain! { + // self_data is not neccessarilly self, to also lint sub-getters, etc… + + let block_expr = if_chain! { if let ExprKind::Block(block,_) = body.value.kind; if block.stmts.is_empty(); if let Some(block_expr) = block.expr; - // replace with while for as many addrof needed - if let ExprKind::AddrOf(_,_, expr) = block_expr.kind; + then { + block_expr + } else { + return; + } + }; + let expr_span = block_expr.span; + + let mut expr = block_expr; + if let ExprKind::AddrOf(_, _, tmp) = expr.kind { + expr = tmp; + } + let (self_data, used_ident) = if_chain! { if let ExprKind::Field(self_data, ident) = expr.kind; if ident.name.as_str() != name; then { - (self_data,ident,block_expr.span) + (self_data,ident) } else { return; } @@ -108,12 +121,12 @@ pub fn check_fn( }; if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) { - let snippet = snippet(cx, span, ".."); + let snippet = snippet(cx, expr_span, ".."); let sugg = format!("{}{name}", snippet.strip_suffix(used_field.name.as_str()).unwrap()); span_lint_and_sugg( cx, MISSNAMED_GETTERS, - span, + expr_span, "getter function appears to return the wrong field", "consider using", sugg, diff --git a/tests/ui/missnamed_getters.rs b/tests/ui/missnamed_getters.rs index b47f6edc5ba1..f9c2351f833d 100644 --- a/tests/ui/missnamed_getters.rs +++ b/tests/ui/missnamed_getters.rs @@ -4,12 +4,32 @@ struct A { a: u8, b: u8, + c: u8, } impl A { fn a(&self) -> &u8 { &self.b } + fn a_mut(&mut self) -> &mut u8 { + &mut self.b + } + + fn b(self) -> u8 { + self.a + } + + fn b_mut(&mut self) -> &mut u8 { + &mut self.a + } + + fn c(&self) -> &u8 { + &self.b + } + + fn c_mut(&mut self) -> &mut u8 { + &mut self.a + } } union B { @@ -21,6 +41,25 @@ impl B { unsafe fn a(&self) -> &u8 { &self.b } + unsafe fn a_mut(&mut self) -> &mut u8 { + &mut self.b + } + + unsafe fn b(self) -> u8 { + self.a + } + + unsafe fn b_mut(&mut self) -> &mut u8 { + &mut self.a + } + + unsafe fn c(&self) -> &u8 { + &self.b + } + + unsafe fn c_mut(&mut self) -> &mut u8 { + &mut self.a + } } fn main() { diff --git a/tests/ui/missnamed_getters.stderr b/tests/ui/missnamed_getters.stderr index 8e31a42b97c1..276096ade87f 100644 --- a/tests/ui/missnamed_getters.stderr +++ b/tests/ui/missnamed_getters.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:11:9 + --> $DIR/missnamed_getters.rs:12:9 | LL | &self.b | ^^^^^^^ help: consider using: `&self.a` @@ -7,10 +7,58 @@ LL | &self.b = note: `-D clippy::missnamed-getters` implied by `-D warnings` error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:22:9 + --> $DIR/missnamed_getters.rs:15:9 + | +LL | &mut self.b + | ^^^^^^^^^^^ help: consider using: `&mut self.a` + +error: getter function appears to return the wrong field + --> $DIR/missnamed_getters.rs:19:9 + | +LL | self.a + | ^^^^^^ help: consider using: `self.b` + +error: getter function appears to return the wrong field + --> $DIR/missnamed_getters.rs:23:9 + | +LL | &mut self.a + | ^^^^^^^^^^^ help: consider using: `&mut self.b` + +error: getter function appears to return the wrong field + --> $DIR/missnamed_getters.rs:27:9 + | +LL | &self.b + | ^^^^^^^ help: consider using: `&self.c` + +error: getter function appears to return the wrong field + --> $DIR/missnamed_getters.rs:31:9 + | +LL | &mut self.a + | ^^^^^^^^^^^ help: consider using: `&mut self.c` + +error: getter function appears to return the wrong field + --> $DIR/missnamed_getters.rs:42:9 | LL | &self.b | ^^^^^^^ help: consider using: `&self.a` -error: aborting due to 2 previous errors +error: getter function appears to return the wrong field + --> $DIR/missnamed_getters.rs:45:9 + | +LL | &mut self.b + | ^^^^^^^^^^^ help: consider using: `&mut self.a` + +error: getter function appears to return the wrong field + --> $DIR/missnamed_getters.rs:49:9 + | +LL | self.a + | ^^^^^^ help: consider using: `self.b` + +error: getter function appears to return the wrong field + --> $DIR/missnamed_getters.rs:53:9 + | +LL | &mut self.a + | ^^^^^^^^^^^ help: consider using: `&mut self.b` + +error: aborting due to 10 previous errors From ddc49966dc4552b77582cf7e5aace8ac97d736fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Tue, 1 Nov 2022 19:28:06 +0100 Subject: [PATCH 213/524] Fix suggestion to point to the whole method --- .../src/functions/missnamed_getters.rs | 22 ++--- tests/ui/missnamed_getters.stderr | 90 ++++++++++++------- 2 files changed, 72 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/functions/missnamed_getters.rs b/clippy_lints/src/functions/missnamed_getters.rs index 1f4eefc620bd..60922fb4ea49 100644 --- a/clippy_lints/src/functions/missnamed_getters.rs +++ b/clippy_lints/src/functions/missnamed_getters.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind}; @@ -13,7 +13,7 @@ pub fn check_fn( kind: FnKind<'_>, decl: &FnDecl<'_>, body: &Body<'_>, - _span: Span, + span: Span, _hir_id: HirId, ) { let FnKind::Method(ref ident, sig) = kind else { @@ -55,6 +55,7 @@ pub fn check_fn( let expr_span = block_expr.span; let mut expr = block_expr; + // Accept &, &mut and if let ExprKind::AddrOf(_, _, tmp) = expr.kind { expr = tmp; } @@ -62,7 +63,7 @@ pub fn check_fn( if let ExprKind::Field(self_data, ident) = expr.kind; if ident.name.as_str() != name; then { - (self_data,ident) + (self_data, ident) } else { return; } @@ -121,16 +122,17 @@ pub fn check_fn( }; if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) { - let snippet = snippet(cx, expr_span, ".."); - let sugg = format!("{}{name}", snippet.strip_suffix(used_field.name.as_str()).unwrap()); - span_lint_and_sugg( + let left_span = block_expr.span.until(used_ident.span); + let snippet = snippet(cx, left_span, ".."); + let sugg = format!("{snippet}{name}"); + span_lint_and_then( cx, MISSNAMED_GETTERS, - expr_span, + span, "getter function appears to return the wrong field", - "consider using", - sugg, - Applicability::MaybeIncorrect, + |diag| { + diag.span_suggestion(expr_span, "consider using", sugg, Applicability::MaybeIncorrect); + }, ); } } diff --git a/tests/ui/missnamed_getters.stderr b/tests/ui/missnamed_getters.stderr index 276096ade87f..2e3d9df34f45 100644 --- a/tests/ui/missnamed_getters.stderr +++ b/tests/ui/missnamed_getters.stderr @@ -1,64 +1,94 @@ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:12:9 + --> $DIR/missnamed_getters.rs:11:5 | -LL | &self.b - | ^^^^^^^ help: consider using: `&self.a` +LL | / fn a(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ | = note: `-D clippy::missnamed-getters` implied by `-D warnings` error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:15:9 + --> $DIR/missnamed_getters.rs:14:5 | -LL | &mut self.b - | ^^^^^^^^^^^ help: consider using: `&mut self.a` +LL | / fn a_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:19:9 + --> $DIR/missnamed_getters.rs:18:5 | -LL | self.a - | ^^^^^^ help: consider using: `self.b` +LL | / fn b(self) -> u8 { +LL | | self.a + | | ------ help: consider using: `self.b` +LL | | } + | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:23:9 + --> $DIR/missnamed_getters.rs:22:5 | -LL | &mut self.a - | ^^^^^^^^^^^ help: consider using: `&mut self.b` +LL | / fn b_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.b` +LL | | } + | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:27:9 + --> $DIR/missnamed_getters.rs:26:5 | -LL | &self.b - | ^^^^^^^ help: consider using: `&self.c` +LL | / fn c(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.c` +LL | | } + | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:31:9 + --> $DIR/missnamed_getters.rs:30:5 | -LL | &mut self.a - | ^^^^^^^^^^^ help: consider using: `&mut self.c` +LL | / fn c_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.c` +LL | | } + | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:42:9 + --> $DIR/missnamed_getters.rs:41:5 | -LL | &self.b - | ^^^^^^^ help: consider using: `&self.a` +LL | / unsafe fn a(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:45:9 + --> $DIR/missnamed_getters.rs:44:5 | -LL | &mut self.b - | ^^^^^^^^^^^ help: consider using: `&mut self.a` +LL | / unsafe fn a_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:49:9 + --> $DIR/missnamed_getters.rs:48:5 | -LL | self.a - | ^^^^^^ help: consider using: `self.b` +LL | / unsafe fn b(self) -> u8 { +LL | | self.a + | | ------ help: consider using: `self.b` +LL | | } + | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:53:9 + --> $DIR/missnamed_getters.rs:52:5 | -LL | &mut self.a - | ^^^^^^^^^^^ help: consider using: `&mut self.b` +LL | / unsafe fn b_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.b` +LL | | } + | |_____^ error: aborting due to 10 previous errors From 81d459083499c35700f3d7fa2b28cdae2f35a636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Tue, 1 Nov 2022 19:31:47 +0100 Subject: [PATCH 214/524] missnamed_getters: use all_fields iterator --- clippy_lints/src/functions/missnamed_getters.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/clippy_lints/src/functions/missnamed_getters.rs b/clippy_lints/src/functions/missnamed_getters.rs index 60922fb4ea49..306dccdbd489 100644 --- a/clippy_lints/src/functions/missnamed_getters.rs +++ b/clippy_lints/src/functions/missnamed_getters.rs @@ -83,24 +83,9 @@ pub fn check_fn( } }; - let variants = def.variants(); - - // We're accessing a field, so it should be an union or a struct and have one and only one variant - if variants.len() != 1 { - if cfg!(debug_assertions) { - panic!("Struct or union expected to have only one variant"); - } else { - // Don't ICE when possible - return; - } - } - - let first = variants.last().unwrap(); - let fields = &variants[first]; - let mut used_field = None; let mut correct_field = None; - for f in &fields.fields { + for f in def.all_fields() { if f.name.as_str() == name { correct_field = Some(f); } From 5fa0e07cdf5e6798a71ff252f14a2713699ae99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Tue, 1 Nov 2022 21:36:18 +0100 Subject: [PATCH 215/524] Document missname_getters --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/functions/mod.rs | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 0c9ae6380d87..bccf8425968b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -177,6 +177,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO, crate::from_str_radix_10::FROM_STR_RADIX_10_INFO, crate::functions::DOUBLE_MUST_USE_INFO, + crate::functions::MISSNAMED_GETTERS_INFO, crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, @@ -184,7 +185,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::functions::RESULT_UNIT_ERR_INFO, crate::functions::TOO_MANY_ARGUMENTS_INFO, crate::functions::TOO_MANY_LINES_INFO, - crate::functions::MISSNAMED_GETTERS_INFO, crate::future_not_send::FUTURE_NOT_SEND_INFO, crate::if_let_mutex::IF_LET_MUTEX_INFO, crate::if_not_else::IF_NOT_ELSE_INFO, diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 726df02444fc..87792899b25b 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -263,21 +263,38 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for getter methods that return a field that doesn't correspond + /// to the name of the method, when there is a field's whose name matches that of the method. /// /// ### Why is this bad? + /// It is most likely that such a method is a bug caused by a typo or by copy-pasting. /// /// ### Example /// ```rust + /// struct A { + /// a: String, + /// b: String, + /// } + /// + /// impl A { + /// fn a(&self) -> &str{ + /// self.b + /// } + /// } /// // example code where clippy issues a warning /// ``` /// Use instead: /// ```rust - /// // example code which does not raise clippy warning + /// impl A { + /// fn a(&self) -> &str{ + /// self.a + /// } + /// } /// ``` #[clippy::version = "1.66.0"] pub MISSNAMED_GETTERS, suspicious, - "default lint description" + "getter method returning the wrong field" } #[derive(Copy, Clone)] From 3e2e81b2db9bbacc826ac429b52ac1173356a2e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Tue, 1 Nov 2022 21:49:20 +0100 Subject: [PATCH 216/524] Fix internal warnings --- clippy_lints/src/functions/missnamed_getters.rs | 5 ++--- clippy_lints/src/functions/mod.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/functions/missnamed_getters.rs b/clippy_lints/src/functions/missnamed_getters.rs index 306dccdbd489..3d78679b526f 100644 --- a/clippy_lints/src/functions/missnamed_getters.rs +++ b/clippy_lints/src/functions/missnamed_getters.rs @@ -28,15 +28,14 @@ pub fn check_fn( let name = ident.name.as_str(); let name = match sig.decl.implicit_self { - ImplicitSelfKind::ImmRef => name, ImplicitSelfKind::MutRef => { let Some(name) = name.strip_suffix("_mut") else { return; }; name }, - ImplicitSelfKind::Imm | ImplicitSelfKind::Mut => name, - _ => return, + ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name, + ImplicitSelfKind::None => return, }; // Body must be &(mut) .name diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 87792899b25b..e4181a164cb7 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -278,16 +278,21 @@ declare_clippy_lint! { /// /// impl A { /// fn a(&self) -> &str{ - /// self.b + /// &self.b /// } /// } /// // example code where clippy issues a warning /// ``` /// Use instead: /// ```rust + /// struct A { + /// a: String, + /// b: String, + /// } + /// /// impl A { /// fn a(&self) -> &str{ - /// self.a + /// &self.a /// } /// } /// ``` From 3428da6e00c025b3b3141a9fe7c65ee5008e07f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 2 Nov 2022 09:01:33 +0100 Subject: [PATCH 217/524] Fix typo missnamed -> misnamed --- CHANGELOG.md | 2 +- clippy_lints/src/declared_lints.rs | 2 +- ...ssnamed_getters.rs => misnamed_getters.rs} | 4 ++-- clippy_lints/src/functions/mod.rs | 8 +++---- ...ssnamed_getters.rs => misnamed_getters.rs} | 2 +- ...getters.stderr => misnamed_getters.stderr} | 22 +++++++++---------- 6 files changed, 20 insertions(+), 20 deletions(-) rename clippy_lints/src/functions/{missnamed_getters.rs => misnamed_getters.rs} (98%) rename tests/ui/{missnamed_getters.rs => misnamed_getters.rs} (96%) rename tests/ui/{missnamed_getters.stderr => misnamed_getters.stderr} (83%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 180e7d8bedcd..5b6b12c623af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4188,6 +4188,7 @@ Released 2018-09-13 [`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute [`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os [`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order +[`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items @@ -4198,7 +4199,6 @@ Released 2018-09-13 [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop [`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods -[`missnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#missnamed_getters [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals [`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index bccf8425968b..eb3210946f11 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -177,7 +177,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO, crate::from_str_radix_10::FROM_STR_RADIX_10_INFO, crate::functions::DOUBLE_MUST_USE_INFO, - crate::functions::MISSNAMED_GETTERS_INFO, + crate::functions::MISNAMED_GETTERS_INFO, crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, diff --git a/clippy_lints/src/functions/missnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs similarity index 98% rename from clippy_lints/src/functions/missnamed_getters.rs rename to clippy_lints/src/functions/misnamed_getters.rs index 3d78679b526f..3859c7a62ea4 100644 --- a/clippy_lints/src/functions/missnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -6,7 +6,7 @@ use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; -use super::MISSNAMED_GETTERS; +use super::MISNAMED_GETTERS; pub fn check_fn( cx: &LateContext<'_>, @@ -111,7 +111,7 @@ pub fn check_fn( let sugg = format!("{snippet}{name}"); span_lint_and_then( cx, - MISSNAMED_GETTERS, + MISNAMED_GETTERS, span, "getter function appears to return the wrong field", |diag| { diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index e4181a164cb7..3478fdd8624a 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,4 +1,4 @@ -mod missnamed_getters; +mod misnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; mod result; @@ -297,7 +297,7 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "1.66.0"] - pub MISSNAMED_GETTERS, + pub MISNAMED_GETTERS, suspicious, "getter method returning the wrong field" } @@ -328,7 +328,7 @@ impl_lint_pass!(Functions => [ MUST_USE_CANDIDATE, RESULT_UNIT_ERR, RESULT_LARGE_ERR, - MISSNAMED_GETTERS, + MISNAMED_GETTERS, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -344,7 +344,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); - missnamed_getters::check_fn(cx, kind, decl, body, span, hir_id); + misnamed_getters::check_fn(cx, kind, decl, body, span, hir_id); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { diff --git a/tests/ui/missnamed_getters.rs b/tests/ui/misnamed_getters.rs similarity index 96% rename from tests/ui/missnamed_getters.rs rename to tests/ui/misnamed_getters.rs index f9c2351f833d..7d490f4b143c 100644 --- a/tests/ui/missnamed_getters.rs +++ b/tests/ui/misnamed_getters.rs @@ -1,5 +1,5 @@ #![allow(unused)] -#![warn(clippy::missnamed_getters)] +#![warn(clippy::misnamed_getters)] struct A { a: u8, diff --git a/tests/ui/missnamed_getters.stderr b/tests/ui/misnamed_getters.stderr similarity index 83% rename from tests/ui/missnamed_getters.stderr rename to tests/ui/misnamed_getters.stderr index 2e3d9df34f45..f4a059ab9b55 100644 --- a/tests/ui/missnamed_getters.stderr +++ b/tests/ui/misnamed_getters.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:11:5 + --> $DIR/misnamed_getters.rs:11:5 | LL | / fn a(&self) -> &u8 { LL | | &self.b @@ -7,10 +7,10 @@ LL | | &self.b LL | | } | |_____^ | - = note: `-D clippy::missnamed-getters` implied by `-D warnings` + = note: `-D clippy::misnamed-getters` implied by `-D warnings` error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:14:5 + --> $DIR/misnamed_getters.rs:14:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | &mut self.b @@ -19,7 +19,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:18:5 + --> $DIR/misnamed_getters.rs:18:5 | LL | / fn b(self) -> u8 { LL | | self.a @@ -28,7 +28,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:22:5 + --> $DIR/misnamed_getters.rs:22:5 | LL | / fn b_mut(&mut self) -> &mut u8 { LL | | &mut self.a @@ -37,7 +37,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:26:5 + --> $DIR/misnamed_getters.rs:26:5 | LL | / fn c(&self) -> &u8 { LL | | &self.b @@ -46,7 +46,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:30:5 + --> $DIR/misnamed_getters.rs:30:5 | LL | / fn c_mut(&mut self) -> &mut u8 { LL | | &mut self.a @@ -55,7 +55,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:41:5 + --> $DIR/misnamed_getters.rs:41:5 | LL | / unsafe fn a(&self) -> &u8 { LL | | &self.b @@ -64,7 +64,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:44:5 + --> $DIR/misnamed_getters.rs:44:5 | LL | / unsafe fn a_mut(&mut self) -> &mut u8 { LL | | &mut self.b @@ -73,7 +73,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:48:5 + --> $DIR/misnamed_getters.rs:48:5 | LL | / unsafe fn b(self) -> u8 { LL | | self.a @@ -82,7 +82,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/missnamed_getters.rs:52:5 + --> $DIR/misnamed_getters.rs:52:5 | LL | / unsafe fn b_mut(&mut self) -> &mut u8 { LL | | &mut self.a From a867c17ab36926a575a57e24a03e99db672055f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 2 Nov 2022 19:02:46 +0100 Subject: [PATCH 218/524] Improve code --- .../src/functions/misnamed_getters.rs | 13 ++++++----- tests/ui/misnamed_getters.rs | 23 +++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index 3859c7a62ea4..0d50ec37989b 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -16,7 +16,7 @@ pub fn check_fn( span: Span, _hir_id: HirId, ) { - let FnKind::Method(ref ident, sig) = kind else { + let FnKind::Method(ref ident, _) = kind else { return; }; @@ -27,7 +27,7 @@ pub fn check_fn( let name = ident.name.as_str(); - let name = match sig.decl.implicit_self { + let name = match decl.implicit_self { ImplicitSelfKind::MutRef => { let Some(name) = name.strip_suffix("_mut") else { return; @@ -53,11 +53,12 @@ pub fn check_fn( }; let expr_span = block_expr.span; - let mut expr = block_expr; // Accept &, &mut and - if let ExprKind::AddrOf(_, _, tmp) = expr.kind { - expr = tmp; - } + let expr = if let ExprKind::AddrOf(_, _, tmp) = block_expr.kind { + tmp + } else { + block_expr + }; let (self_data, used_ident) = if_chain! { if let ExprKind::Field(self_data, ident) = expr.kind; if ident.name.as_str() != name; diff --git a/tests/ui/misnamed_getters.rs b/tests/ui/misnamed_getters.rs index 7d490f4b143c..6f1618182516 100644 --- a/tests/ui/misnamed_getters.rs +++ b/tests/ui/misnamed_getters.rs @@ -60,6 +60,29 @@ impl B { unsafe fn c_mut(&mut self) -> &mut u8 { &mut self.a } + + unsafe fn a_unchecked(&self) -> &u8 { + &self.b + } + unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { + &mut self.b + } + + unsafe fn b_unchecked(self) -> u8 { + self.a + } + + unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { + &mut self.a + } + + unsafe fn c_unchecked(&self) -> &u8 { + &self.b + } + + unsafe fn c_unchecked_mut(&mut self) -> &mut u8 { + &mut self.a + } } fn main() { From 3f1a186bd1fd74ad3b25bf5c642e86c8913a1149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 2 Nov 2022 19:11:27 +0100 Subject: [PATCH 219/524] misnamed_getters: Trigger on unsafe with _unchecked --- .../src/functions/misnamed_getters.rs | 10 ++++- tests/ui/misnamed_getters.stderr | 38 ++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index 0d50ec37989b..599269e2d631 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::Applicability; -use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind}; +use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind, Unsafety}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; @@ -16,7 +16,7 @@ pub fn check_fn( span: Span, _hir_id: HirId, ) { - let FnKind::Method(ref ident, _) = kind else { + let FnKind::Method(ref ident, sig) = kind else { return; }; @@ -38,6 +38,12 @@ pub fn check_fn( ImplicitSelfKind::None => return, }; + let name = if sig.header.unsafety == Unsafety::Unsafe { + name.strip_suffix("_unchecked").unwrap_or(name) + } else { + name + }; + // Body must be &(mut) .name // self_data is not neccessarilly self, to also lint sub-getters, etc… diff --git a/tests/ui/misnamed_getters.stderr b/tests/ui/misnamed_getters.stderr index f4a059ab9b55..feefb95ab4f8 100644 --- a/tests/ui/misnamed_getters.stderr +++ b/tests/ui/misnamed_getters.stderr @@ -90,5 +90,41 @@ LL | | &mut self.a LL | | } | |_____^ -error: aborting due to 10 previous errors +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:64:5 + | +LL | / unsafe fn a_unchecked(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:67:5 + | +LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:71:5 + | +LL | / unsafe fn b_unchecked(self) -> u8 { +LL | | self.a + | | ------ help: consider using: `self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:75:5 + | +LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.b` +LL | | } + | |_____^ + +error: aborting due to 14 previous errors From 77374a95272dfb8fcbc75cd074b4e61b1890c724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Sat, 12 Nov 2022 22:33:46 +0100 Subject: [PATCH 220/524] Add failing test --- tests/ui/misnamed_getters.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/ui/misnamed_getters.rs b/tests/ui/misnamed_getters.rs index 6f1618182516..2c451bd3a162 100644 --- a/tests/ui/misnamed_getters.rs +++ b/tests/ui/misnamed_getters.rs @@ -85,6 +85,18 @@ impl B { } } +struct C { + inner: Box, +} +impl C { + unsafe fn a(&self) -> &u8 { + &self.inner.b + } + unsafe fn a_mut(&mut self) -> &mut u8 { + &mut self.inner.b + } +} + fn main() { // test code goes here } From d9993cb133909eb17705cd9dc518c2c213fc779b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Sun, 20 Nov 2022 12:20:16 +0100 Subject: [PATCH 221/524] Remove error when fields use autoderef --- clippy_lints/src/functions/misnamed_getters.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index 599269e2d631..c553901059bb 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -101,16 +101,14 @@ pub fn check_fn( } let Some(used_field) = used_field else { - if cfg!(debug_assertions) { - panic!("Struct doesn't contain the correct field"); - } else { - // Don't ICE when possible - return; - } - }; + // FIXME: This can be reached if the field access uses autoderef. + // `dec.all_fields()` should be replaced by something that uses autoderef. + return; + }; + let Some(correct_field) = correct_field else { return; - }; + }; if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) { let left_span = block_expr.span.until(used_ident.span); From 6178ddaded7f73ba413475ea27ac98336069cb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Sun, 20 Nov 2022 13:46:30 +0100 Subject: [PATCH 222/524] misname-getters: Fix documentation --- clippy_lints/src/functions/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 3478fdd8624a..91e6ffe64479 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -270,6 +270,7 @@ declare_clippy_lint! { /// It is most likely that such a method is a bug caused by a typo or by copy-pasting. /// /// ### Example + /// ```rust /// struct A { /// a: String, @@ -281,7 +282,7 @@ declare_clippy_lint! { /// &self.b /// } /// } - /// // example code where clippy issues a warning + /// ``` /// Use instead: /// ```rust @@ -296,7 +297,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.67.0"] pub MISNAMED_GETTERS, suspicious, "getter method returning the wrong field" From 0411edfbbda82407c956b69db5bf687f8749766e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Sun, 20 Nov 2022 15:34:56 +0100 Subject: [PATCH 223/524] Improve diagnostic for cases where autoderef is used --- .../src/functions/misnamed_getters.rs | 4 ++-- tests/ui/misnamed_getters.stderr | 20 ++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index c553901059bb..5424c0eecfed 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -75,7 +75,7 @@ pub fn check_fn( } }; - let ty = cx.typeck_results().expr_ty(self_data); + let ty = cx.typeck_results().expr_ty_adjusted(self_data); let def = { let mut kind = ty.kind(); @@ -102,7 +102,7 @@ pub fn check_fn( let Some(used_field) = used_field else { // FIXME: This can be reached if the field access uses autoderef. - // `dec.all_fields()` should be replaced by something that uses autoderef. + // `dec.all_fields()` should be replaced by something that uses autoderef on the unajusted type of `self_data` return; }; diff --git a/tests/ui/misnamed_getters.stderr b/tests/ui/misnamed_getters.stderr index feefb95ab4f8..5210295893e8 100644 --- a/tests/ui/misnamed_getters.stderr +++ b/tests/ui/misnamed_getters.stderr @@ -126,5 +126,23 @@ LL | | &mut self.a LL | | } | |_____^ -error: aborting due to 14 previous errors +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:92:5 + | +LL | / unsafe fn a(&self) -> &u8 { +LL | | &self.inner.b + | | ------------- help: consider using: `&self.inner.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:95:5 + | +LL | / unsafe fn a_mut(&mut self) -> &mut u8 { +LL | | &mut self.inner.b + | | ----------------- help: consider using: `&mut self.inner.a` +LL | | } + | |_____^ + +error: aborting due to 16 previous errors From 1f2f50c34eb304ce56213dad70ccdeb2f2901512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Sun, 20 Nov 2022 17:03:53 +0100 Subject: [PATCH 224/524] Fix many false negatives caused by autoderef --- .../src/functions/misnamed_getters.rs | 44 +++++++++---------- tests/ui/misnamed_getters.rs | 36 ++++++++++++--- tests/ui/misnamed_getters.stderr | 36 +++++++++++---- 3 files changed, 77 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs index 5424c0eecfed..27acad45ccf7 100644 --- a/clippy_lints/src/functions/misnamed_getters.rs +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -6,6 +6,8 @@ use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; +use std::iter; + use super::MISNAMED_GETTERS; pub fn check_fn( @@ -75,39 +77,35 @@ pub fn check_fn( } }; - let ty = cx.typeck_results().expr_ty_adjusted(self_data); - - let def = { - let mut kind = ty.kind(); - loop { - match kind { - ty::Adt(def, _) => break def, - ty::Ref(_, ty, _) => kind = ty.kind(), - // We don't do tuples because the function name cannot be a number - _ => return, - } - } - }; - let mut used_field = None; let mut correct_field = None; - for f in def.all_fields() { - if f.name.as_str() == name { - correct_field = Some(f); - } - if f.name == used_ident.name { - used_field = Some(f); + let typeck_results = cx.typeck_results(); + for adjusted_type in iter::once(typeck_results.expr_ty(self_data)) + .chain(typeck_results.expr_adjustments(self_data).iter().map(|adj| adj.target)) + { + let ty::Adt(def,_) = adjusted_type.kind() else { + continue; + }; + + for f in def.all_fields() { + if f.name.as_str() == name { + correct_field = Some(f); + } + if f.name == used_ident.name { + used_field = Some(f); + } } } let Some(used_field) = used_field else { - // FIXME: This can be reached if the field access uses autoderef. - // `dec.all_fields()` should be replaced by something that uses autoderef on the unajusted type of `self_data` + // Can happen if the field access is a tuple. We don't lint those because the getter name could not start with a number. return; }; let Some(correct_field) = correct_field else { - return; + // There is no field corresponding to the getter name. + // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is + return; }; if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) { diff --git a/tests/ui/misnamed_getters.rs b/tests/ui/misnamed_getters.rs index 2c451bd3a162..03e7dac7df94 100644 --- a/tests/ui/misnamed_getters.rs +++ b/tests/ui/misnamed_getters.rs @@ -85,15 +85,37 @@ impl B { } } -struct C { - inner: Box, +struct D { + d: u8, + inner: A, } -impl C { - unsafe fn a(&self) -> &u8 { - &self.inner.b + +impl core::ops::Deref for D { + type Target = A; + fn deref(&self) -> &A { + &self.inner } - unsafe fn a_mut(&mut self) -> &mut u8 { - &mut self.inner.b +} + +impl core::ops::DerefMut for D { + fn deref_mut(&mut self) -> &mut A { + &mut self.inner + } +} + +impl D { + fn a(&self) -> &u8 { + &self.b + } + fn a_mut(&mut self) -> &mut u8 { + &mut self.b + } + + fn d(&self) -> &u8 { + &self.b + } + fn d_mut(&mut self) -> &mut u8 { + &mut self.b } } diff --git a/tests/ui/misnamed_getters.stderr b/tests/ui/misnamed_getters.stderr index 5210295893e8..1e38a83d019a 100644 --- a/tests/ui/misnamed_getters.stderr +++ b/tests/ui/misnamed_getters.stderr @@ -127,22 +127,40 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:92:5 + --> $DIR/misnamed_getters.rs:107:5 | -LL | / unsafe fn a(&self) -> &u8 { -LL | | &self.inner.b - | | ------------- help: consider using: `&self.inner.a` +LL | / fn a(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` LL | | } | |_____^ error: getter function appears to return the wrong field - --> $DIR/misnamed_getters.rs:95:5 + --> $DIR/misnamed_getters.rs:110:5 | -LL | / unsafe fn a_mut(&mut self) -> &mut u8 { -LL | | &mut self.inner.b - | | ----------------- help: consider using: `&mut self.inner.a` +LL | / fn a_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:114:5 + | +LL | / fn d(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.d` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:117:5 + | +LL | / fn d_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.d` LL | | } | |_____^ -error: aborting due to 16 previous errors +error: aborting due to 18 previous errors From ed183ee9acbc8b0c13a7a1a4859db9b4e7ca7d37 Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 29 Oct 2022 21:45:40 +0200 Subject: [PATCH 225/524] Fix [`unnecessary_lazy_eval`] when type has significant drop --- clippy_utils/src/eager_or_lazy.rs | 26 ++++++--- tests/ui/unnecessary_lazy_eval.fixed | 23 +++++--- tests/ui/unnecessary_lazy_eval.rs | 23 +++++--- tests/ui/unnecessary_lazy_eval.stderr | 84 +++++++++++++-------------- 4 files changed, 92 insertions(+), 64 deletions(-) diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 95b3e651e2b5..107405609fd5 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -91,6 +91,16 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: } } +fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res { + cx.typeck_results() + .expr_ty(e) + .has_significant_drop(cx.tcx, cx.param_env) + } else { + false + } +} + #[expect(clippy::too_many_lines)] fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { struct V<'cx, 'tcx> { @@ -113,13 +123,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, args, ) => match self.cx.qpath_res(path, hir_id) { - Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => { - if self - .cx - .typeck_results() - .expr_ty(e) - .has_significant_drop(self.cx.tcx, self.cx.param_env) - { + res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => { + if res_has_significant_drop(res, self.cx, e) { self.eagerness = ForceNoChange; return; } @@ -147,6 +152,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS self.eagerness |= NoChange; return; }, + ExprKind::Path(ref path) => { + if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + self.eagerness = ForceNoChange; + return; + } + }, ExprKind::MethodCall(name, ..) => { self.eagerness |= self .cx @@ -206,7 +217,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS | ExprKind::Match(..) | ExprKind::Closure { .. } | ExprKind::Field(..) - | ExprKind::Path(_) | ExprKind::AddrOf(..) | ExprKind::Struct(..) | ExprKind::Repeat(..) diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index ce4a82e02174..22e9bd8bdc51 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -33,6 +33,14 @@ impl Drop for Issue9427 { } } +struct Issue9427FollowUp; + +impl Drop for Issue9427FollowUp { + fn drop(&mut self) { + panic!("side effect drop"); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -87,6 +95,7 @@ fn main() { // Should not lint - bool let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); @@ -133,13 +142,13 @@ fn main() { let _: Result = res.or(Ok(astronomers_pi)); let _: Result = res.or(Ok(ext_str.some_field)); let _: Result = res. - // some lines - // some lines - // some lines - // some lines - // some lines - // some lines - or(Ok(ext_str.some_field)); + // some lines + // some lines + // some lines + // some lines + // some lines + // some lines + or(Ok(ext_str.some_field)); // neither bind_instead_of_map nor unnecessary_lazy_eval applies here let _: Result = res.and_then(|x| Err(x)); diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 59cdf6628546..8726d84a23fc 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -33,6 +33,14 @@ impl Drop for Issue9427 { } } +struct Issue9427FollowUp; + +impl Drop for Issue9427FollowUp { + fn drop(&mut self) { + panic!("side effect drop"); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -87,6 +95,7 @@ fn main() { // Should not lint - bool let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); @@ -133,13 +142,13 @@ fn main() { let _: Result = res.or_else(|_| Ok(astronomers_pi)); let _: Result = res.or_else(|_| Ok(ext_str.some_field)); let _: Result = res. - // some lines - // some lines - // some lines - // some lines - // some lines - // some lines - or_else(|_| Ok(ext_str.some_field)); + // some lines + // some lines + // some lines + // some lines + // some lines + // some lines + or_else(|_| Ok(ext_str.some_field)); // neither bind_instead_of_map nor unnecessary_lazy_eval applies here let _: Result = res.and_then(|x| Err(x)); diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 8a9ece4aa7e5..0339755442c5 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^-------------------- @@ -9,7 +9,7 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^--------------------------------- @@ -17,7 +17,7 @@ LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^------------------------------------- @@ -25,7 +25,7 @@ LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:60:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^--------------------- @@ -33,7 +33,7 @@ LL | let _ = opt.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:53:13 + --> $DIR/unnecessary_lazy_eval.rs:61:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^------------------- @@ -41,7 +41,7 @@ LL | let _ = opt.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:13 + --> $DIR/unnecessary_lazy_eval.rs:62:13 | LL | let _ = opt.or_else(|| None); | ^^^^---------------- @@ -49,7 +49,7 @@ LL | let _ = opt.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:55:13 + --> $DIR/unnecessary_lazy_eval.rs:63:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^------------------------ @@ -57,7 +57,7 @@ LL | let _ = opt.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:56:13 + --> $DIR/unnecessary_lazy_eval.rs:64:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^---------------- @@ -65,7 +65,7 @@ LL | let _ = opt.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:57:13 + --> $DIR/unnecessary_lazy_eval.rs:65:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | ^^^^^^^^^^^^^^^^^------------------------------- @@ -73,7 +73,7 @@ LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:58:13 + --> $DIR/unnecessary_lazy_eval.rs:66:13 | LL | let _ = cond.then(|| astronomers_pi); | ^^^^^----------------------- @@ -81,7 +81,7 @@ LL | let _ = cond.then(|| astronomers_pi); | help: use `then_some(..)` instead: `then_some(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:61:13 + --> $DIR/unnecessary_lazy_eval.rs:69:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^-------------------- @@ -89,7 +89,7 @@ LL | let _ = Some(10).unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:62:13 + --> $DIR/unnecessary_lazy_eval.rs:70:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^--------------------- @@ -97,7 +97,7 @@ LL | let _ = Some(10).and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:63:28 + --> $DIR/unnecessary_lazy_eval.rs:71:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^------------------- @@ -105,7 +105,7 @@ LL | let _: Option = None.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:64:13 + --> $DIR/unnecessary_lazy_eval.rs:72:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^------------------------ @@ -113,7 +113,7 @@ LL | let _ = None.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:65:35 + --> $DIR/unnecessary_lazy_eval.rs:73:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^---------------- @@ -121,7 +121,7 @@ LL | let _: Result = None.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:66:28 + --> $DIR/unnecessary_lazy_eval.rs:74:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^---------------- @@ -129,7 +129,7 @@ LL | let _: Option = None.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:69:13 + --> $DIR/unnecessary_lazy_eval.rs:77:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^-------------------- @@ -137,7 +137,7 @@ LL | let _ = deep.0.unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:70:13 + --> $DIR/unnecessary_lazy_eval.rs:78:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^--------------------- @@ -145,7 +145,7 @@ LL | let _ = deep.0.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:71:13 + --> $DIR/unnecessary_lazy_eval.rs:79:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^---------------- @@ -153,7 +153,7 @@ LL | let _ = deep.0.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:72:13 + --> $DIR/unnecessary_lazy_eval.rs:80:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^------------------------ @@ -161,7 +161,7 @@ LL | let _ = deep.0.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:73:13 + --> $DIR/unnecessary_lazy_eval.rs:81:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^---------------- @@ -169,7 +169,7 @@ LL | let _ = deep.0.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:96:28 + --> $DIR/unnecessary_lazy_eval.rs:105:28 | LL | let _: Option = None.or_else(|| Some(3)); | ^^^^^------------------- @@ -177,7 +177,7 @@ LL | let _: Option = None.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:97:13 + --> $DIR/unnecessary_lazy_eval.rs:106:13 | LL | let _ = deep.0.or_else(|| Some(3)); | ^^^^^^^------------------- @@ -185,7 +185,7 @@ LL | let _ = deep.0.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:98:13 + --> $DIR/unnecessary_lazy_eval.rs:107:13 | LL | let _ = opt.or_else(|| Some(3)); | ^^^^------------------- @@ -193,7 +193,7 @@ LL | let _ = opt.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:104:13 + --> $DIR/unnecessary_lazy_eval.rs:113:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^--------------------- @@ -201,7 +201,7 @@ LL | let _ = res2.unwrap_or_else(|_| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:105:13 + --> $DIR/unnecessary_lazy_eval.rs:114:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^---------------------------------- @@ -209,7 +209,7 @@ LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:106:13 + --> $DIR/unnecessary_lazy_eval.rs:115:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^-------------------------------------- @@ -217,7 +217,7 @@ LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:128:35 + --> $DIR/unnecessary_lazy_eval.rs:137:35 | LL | let _: Result = res.and_then(|_| Err(2)); | ^^^^-------------------- @@ -225,7 +225,7 @@ LL | let _: Result = res.and_then(|_| Err(2)); | help: use `and(..)` instead: `and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:129:35 + --> $DIR/unnecessary_lazy_eval.rs:138:35 | LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | ^^^^--------------------------------- @@ -233,7 +233,7 @@ LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | help: use `and(..)` instead: `and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:130:35 + --> $DIR/unnecessary_lazy_eval.rs:139:35 | LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); | ^^^^------------------------------------- @@ -241,7 +241,7 @@ LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)) | help: use `and(..)` instead: `and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:132:35 + --> $DIR/unnecessary_lazy_eval.rs:141:35 | LL | let _: Result = res.or_else(|_| Ok(2)); | ^^^^------------------ @@ -249,7 +249,7 @@ LL | let _: Result = res.or_else(|_| Ok(2)); | help: use `or(..)` instead: `or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:133:35 + --> $DIR/unnecessary_lazy_eval.rs:142:35 | LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | ^^^^------------------------------- @@ -257,7 +257,7 @@ LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | help: use `or(..)` instead: `or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:134:35 + --> $DIR/unnecessary_lazy_eval.rs:143:35 | LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | ^^^^----------------------------------- @@ -265,19 +265,19 @@ LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:135:35 + --> $DIR/unnecessary_lazy_eval.rs:144:35 | LL | let _: Result = res. | ___________________________________^ -LL | | // some lines -LL | | // some lines -LL | | // some lines +LL | | // some lines +LL | | // some lines +LL | | // some lines ... | -LL | | // some lines -LL | | or_else(|_| Ok(ext_str.some_field)); - | |_________----------------------------------^ - | | - | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` +LL | | // some lines +LL | | or_else(|_| Ok(ext_str.some_field)); + | |_____----------------------------------^ + | | + | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: aborting due to 34 previous errors From 386d0a5c67921b19e62522e693cba9738a08760e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 2 Nov 2022 15:10:05 +0000 Subject: [PATCH 226/524] Add an always-ambiguous predicate to make sure that we don't accidentlally allow trait resolution to prove false things during coherence --- clippy_utils/src/qualify_min_const_fn.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 45b63a4aa5df..b48bacb9ace6 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -37,6 +37,7 @@ pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"), ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"), ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {predicate:#?}"), + ty::PredicateKind::Ambiguous => panic!("ambiguous predicate on function: {predicate:#?}"), } } match predicates.parent { From 637139d2ff4202f9296777f8f4750bcc233b09f8 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 21 Nov 2022 14:37:51 +0000 Subject: [PATCH 227/524] Add `clippy_utils::msrv::Msrv` to keep track of the current MSRV --- book/src/development/adding_lints.md | 8 +- clippy_dev/src/new_lint.rs | 16 ++- .../src/almost_complete_letter_range.rs | 11 +- clippy_lints/src/approx_const.rs | 8 +- clippy_lints/src/attrs.rs | 12 +- .../src/casts/cast_abs_to_unsigned.rs | 7 +- clippy_lints/src/casts/cast_lossless.rs | 16 +-- .../src/casts/cast_slice_different_sizes.rs | 8 +- .../src/casts/cast_slice_from_raw_parts.rs | 14 +-- clippy_lints/src/casts/mod.rs | 22 ++-- clippy_lints/src/casts/ptr_as_ptr.rs | 7 +- clippy_lints/src/checked_conversions.rs | 10 +- clippy_lints/src/dereference.rs | 18 +-- clippy_lints/src/format_args.rs | 10 +- clippy_lints/src/from_over_into.rs | 10 +- clippy_lints/src/if_then_some_else_none.rs | 14 +-- clippy_lints/src/index_refutable_slice.rs | 11 +- clippy_lints/src/instant_subtraction.rs | 18 ++- clippy_lints/src/lib.rs | 108 ++++++------------ clippy_lints/src/manual_bits.rs | 10 +- clippy_lints/src/manual_clamp.rs | 33 +++--- clippy_lints/src/manual_is_ascii_check.rs | 15 +-- clippy_lints/src/manual_let_else.rs | 10 +- clippy_lints/src/manual_non_exhaustive.rs | 16 +-- clippy_lints/src/manual_rem_euclid.rs | 12 +- clippy_lints/src/manual_retain.rs | 24 ++-- clippy_lints/src/manual_strip.rs | 10 +- clippy_lints/src/matches/mod.rs | 14 +-- clippy_lints/src/mem_replace.rs | 10 +- .../src/methods/cloned_instead_of_copied.rs | 10 +- clippy_lints/src/methods/err_expect.rs | 8 +- clippy_lints/src/methods/filter_map_next.rs | 8 +- .../src/methods/is_digit_ascii_radix.rs | 9 +- clippy_lints/src/methods/map_clone.rs | 16 +-- clippy_lints/src/methods/map_unwrap_or.rs | 7 +- clippy_lints/src/methods/mod.rs | 36 +++--- .../src/methods/option_as_ref_deref.rs | 8 +- clippy_lints/src/methods/str_splitn.rs | 8 +- .../src/methods/unnecessary_to_owned.rs | 9 +- clippy_lints/src/missing_const_for_fn.rs | 14 +-- clippy_lints/src/ranges.rs | 10 +- clippy_lints/src/redundant_field_names.rs | 9 +- .../src/redundant_static_lifetimes.rs | 9 +- clippy_lints/src/transmute/mod.rs | 8 +- .../src/transmute/transmute_ptr_to_ref.rs | 10 +- clippy_lints/src/unnested_or_patterns.rs | 17 ++- clippy_lints/src/use_self.rs | 14 +-- .../utils/internal_lints/msrv_attr_impl.rs | 2 +- clippy_utils/src/lib.rs | 33 +----- clippy_utils/src/msrvs.rs | 101 ++++++++++++++++ clippy_utils/src/paths.rs | 4 +- clippy_utils/src/qualify_min_const_fn.rs | 18 ++- .../ui-internal/invalid_msrv_attr_impl.fixed | 4 +- tests/ui-internal/invalid_msrv_attr_impl.rs | 4 +- tests/ui/min_rust_version_attr.rs | 23 ++++ tests/ui/min_rust_version_attr.stderr | 18 ++- 56 files changed, 466 insertions(+), 433 deletions(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 3c3f368a529b..29d0d45dcc96 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -443,22 +443,22 @@ value is passed to the constructor in `clippy_lints/lib.rs`. ```rust pub struct ManualStrip { - msrv: Option, + msrv: Msrv, } impl ManualStrip { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } ``` The project's MSRV can then be matched against the feature MSRV in the LintPass -using the `meets_msrv` utility function. +using the `Msrv::meets` method. ``` rust -if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { +if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) { return; } ``` diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 9e15f1504fa9..ec7f1dd0d846 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -120,7 +120,7 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let new_lint = if enable_msrv { format!( - "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv)));\n ", + "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv())));\n ", lint_pass = lint.pass, ctor_arg = if lint.pass == "late" { "_" } else { "" }, module_name = lint.name, @@ -238,10 +238,9 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result.push_str(&if enable_msrv { formatdoc!( r#" - use clippy_utils::msrvs; + use clippy_utils::msrvs::{{self, Msrv}}; {pass_import} use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; - use rustc_semver::RustcVersion; use rustc_session::{{declare_tool_lint, impl_lint_pass}}; "# @@ -263,12 +262,12 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { formatdoc!( r#" pub struct {name_camel} {{ - msrv: Option, + msrv: Msrv, }} impl {name_camel} {{ #[must_use] - pub fn new(msrv: Option) -> Self {{ + pub fn new(msrv: Msrv) -> Self {{ Self {{ msrv }} }} }} @@ -357,15 +356,14 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R let _ = writedoc!( lint_file_contents, r#" - use clippy_utils::{{meets_msrv, msrvs}}; + use clippy_utils::msrvs::{{self, Msrv}}; use rustc_lint::{{{context_import}, LintContext}}; - use rustc_semver::RustcVersion; use super::{name_upper}; // TODO: Adjust the parameters as necessary - pub(super) fn check(cx: &{context_import}, msrv: Option) {{ - if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{ + pub(super) fn check(cx: &{context_import}, msrv: &Msrv) {{ + if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{ return; }} todo!(); diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_letter_range.rs index 073e4af1318e..3cf8ba0a5211 100644 --- a/clippy_lints/src/almost_complete_letter_range.rs +++ b/clippy_lints/src/almost_complete_letter_range.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{trim_span, walk_span_to_context}; -use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -33,10 +32,10 @@ declare_clippy_lint! { impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]); pub struct AlmostCompleteLetterRange { - msrv: Option, + msrv: Msrv, } impl AlmostCompleteLetterRange { - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -46,7 +45,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange { let ctxt = e.span.ctxt(); let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt) && let Some(end) = walk_span_to_context(end.span, ctxt) - && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) + && self.msrv.meets(msrvs::RANGE_INCLUSIVE) { Some((trim_span(cx.sess().source_map(), start.between(end)), "..=")) } else { @@ -60,7 +59,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange { if let PatKind::Range(Some(start), Some(end), kind) = &p.kind && matches!(kind.node, RangeEnd::Excluded) { - let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) { + let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) { "..=" } else { "..." diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 724490fb4959..ccf82f132f4e 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -63,12 +63,12 @@ const KNOWN_CONSTS: [(f64, &str, usize, Option); 19] = [ ]; pub struct ApproxConstant { - msrv: Option, + msrv: Msrv, } impl ApproxConstant { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } @@ -87,7 +87,7 @@ impl ApproxConstant { let s = s.as_str(); if s.parse::().is_ok() { for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { - if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) { + if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| self.msrv.meets(msrv)) { span_lint_and_help( cx, APPROX_CONSTANT, diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index ecf8e83375db..54364f9dab21 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -2,9 +2,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, macro_backtrace}; -use clippy_utils::msrvs; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; @@ -14,7 +13,6 @@ 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_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; @@ -599,7 +597,7 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { } pub struct EarlyAttributes { - pub msrv: Option, + pub msrv: Msrv, } impl_lint_pass!(EarlyAttributes => [ @@ -614,7 +612,7 @@ impl EarlyLintPass for EarlyAttributes { } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { - check_deprecated_cfg_attr(cx, attr, self.msrv); + check_deprecated_cfg_attr(cx, attr, &self.msrv); check_mismatched_target_os(cx, attr); } @@ -654,9 +652,9 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It } } -fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option) { +fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { if_chain! { - if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES); + if msrv.meets(msrvs::TOOL_ATTRIBUTES); // check cfg_attr if attr.has_name(sym::cfg_attr); if let Some(items) = attr.meta_item_list(); diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 3f1edabe6c50..442262983337 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use super::CAST_ABS_TO_UNSIGNED; @@ -15,9 +14,9 @@ pub(super) fn check( cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: Option, + msrv: &Msrv, ) { - if meets_msrv(msrv, msrvs::UNSIGNED_ABS) + if msrv.meets(msrvs::UNSIGNED_ABS) && let ty::Int(from) = cast_from.kind() && let ty::Uint(to) = cast_to.kind() && let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 13c403234dad..cf07e050ccce 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_constant; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_isize_or_usize; -use clippy_utils::{in_constant, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use rustc_semver::RustcVersion; use super::{utils, CAST_LOSSLESS}; @@ -16,7 +16,7 @@ pub(super) fn check( cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: Option, + msrv: &Msrv, ) { if !should_lint(cx, expr, cast_from, cast_to, msrv) { return; @@ -57,13 +57,7 @@ pub(super) fn check( ); } -fn should_lint( - cx: &LateContext<'_>, - expr: &Expr<'_>, - cast_from: Ty<'_>, - cast_to: Ty<'_>, - msrv: Option, -) -> bool { +fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). if in_constant(cx, expr.hir_id) { return false; @@ -89,7 +83,7 @@ fn should_lint( }; !is_isize_or_usize(cast_from) && from_nbits < to_nbits }, - (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true, + (false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(msrvs::FROM_BOOL) => true, (_, _) => { matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) }, diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index d31d10d22b92..e862f13e69fc 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,16 +1,16 @@ -use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{diagnostics::span_lint_and_then, source}; use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut}; -use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv) { // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist - if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) { + if !msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) { return; } diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 284ef165998a..627b795d6edd 100644 --- a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use super::CAST_SLICE_FROM_RAW_PARTS; @@ -25,15 +25,9 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option { } } -pub(super) fn check( - cx: &LateContext<'_>, - expr: &Expr<'_>, - cast_expr: &Expr<'_>, - cast_to: Ty<'_>, - msrv: Option, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) { if_chain! { - if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS); + if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS); if let ty::RawPtr(ptrty) = cast_to.kind(); if let ty::Slice(_) = ptrty.ty.kind(); if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind; diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 7148b5e6ebf4..c6d505c4a181 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -21,11 +21,11 @@ mod ptr_as_ptr; mod unnecessary_cast; mod utils; -use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs}; +use clippy_utils::is_hir_ty_cfg_dependant; +use clippy_utils::msrvs::{self, Msrv}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -648,12 +648,12 @@ declare_clippy_lint! { } pub struct Casts { - msrv: Option, + msrv: Msrv, } impl Casts { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -686,7 +686,7 @@ impl_lint_pass!(Casts => [ impl<'tcx> LateLintPass<'tcx> for Casts { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !in_external_macro(cx.sess(), expr.span) { - ptr_as_ptr::check(cx, expr, self.msrv); + ptr_as_ptr::check(cx, expr, &self.msrv); } if expr.span.from_expansion() { @@ -705,7 +705,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) { return; } - cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv); + cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); @@ -717,16 +717,16 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); - cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } as_underscore::check(cx, expr, cast_to_hir); - if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) { + if self.msrv.meets(msrvs::BORROW_AS_PTR) { borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir); } } @@ -734,8 +734,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_ref_to_mut::check(cx, expr); cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); - ptr_as_ptr::check(cx, expr, self.msrv); - cast_slice_different_sizes::check(cx, expr, self.msrv); + ptr_as_ptr::check(cx, expr, &self.msrv); + cast_slice_different_sizes::check(cx, expr, &self.msrv); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index c2b9253ec35d..43d75a03235e 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -1,19 +1,18 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, TypeAndMut}; -use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option) { - if !meets_msrv(msrv, msrvs::POINTER_CAST) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { + if !msrv.meets(msrvs::POINTER_CAST) { return; } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 78e9921f036f..9102a89e3772 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -1,14 +1,14 @@ //! lint on manually implemented checked conversions that could be transformed into `try_from` use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq}; +use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; use if_chain::if_chain; 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_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -37,12 +37,12 @@ declare_clippy_lint! { } pub struct CheckedConversions { - msrv: Option, + msrv: Msrv, } impl CheckedConversions { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -51,7 +51,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::TRY_FROM) { + if !self.msrv.meets(msrvs::TRY_FROM) { return; } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 0eee7f23982d..faa8467d2661 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; use clippy_utils::{ - fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local, - walk_to_expr_usage, + fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage, }; + use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; @@ -28,7 +29,6 @@ use rustc_middle::ty::{ self, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults, }; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt as _; @@ -181,12 +181,12 @@ pub struct Dereferencing<'tcx> { possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, // `IntoIterator` for arrays requires Rust 1.53. - msrv: Option, + msrv: Msrv, } impl<'tcx> Dereferencing<'tcx> { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Dereferencing::default() @@ -286,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv); + let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv); match kind { RefOp::Deref => { if let Position::FieldAccess { @@ -698,7 +698,7 @@ fn walk_parents<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, e: &'tcx Expr<'_>, - msrv: Option, + msrv: &Msrv, ) -> (Position, &'tcx [Adjustment<'tcx>]) { let mut adjustments = [].as_slice(); let mut precedence = 0i8; @@ -1082,7 +1082,7 @@ fn needless_borrow_impl_arg_position<'tcx>( param_ty: ParamTy, mut expr: &Expr<'tcx>, precedence: i8, - msrv: Option, + msrv: &Msrv, ) -> Position { let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); @@ -1182,7 +1182,7 @@ fn needless_borrow_impl_arg_position<'tcx>( && let ty::Param(param_ty) = trait_predicate.self_ty().kind() && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack() && ty.is_array() - && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR) + && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) { return false; } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index a73453bf0274..ec45be558f14 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::is_diag_trait_item; use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; use clippy_utils::macros::{ is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage, }; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; use if_chain::if_chain; use itertools::Itertools; use rustc_errors::Applicability; @@ -13,7 +14,6 @@ use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::DefId; use rustc_span::edition::Edition::Edition2021; @@ -158,12 +158,12 @@ impl_lint_pass!(FormatArgs => [ ]); pub struct FormatArgs { - msrv: Option, + msrv: Msrv, } impl FormatArgs { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -188,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); check_to_string_in_format_args(cx, name, arg.param.value); } - if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { + if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id); } } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 95eda4ea8827..7ff4a05fcdd6 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::path_def_id; use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs, path_def_id}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_path, Visitor}; use rustc_hir::{ @@ -10,7 +11,6 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, sym}; use rustc_span::{Span, Symbol}; @@ -49,12 +49,12 @@ declare_clippy_lint! { } pub struct FromOverInto { - msrv: Option, + msrv: Msrv, } impl FromOverInto { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { FromOverInto { msrv } } } @@ -63,7 +63,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { + if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { return; } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 0d6718c168a5..9cadaaa493e4 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,14 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{ - contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks, -}; +use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -47,12 +45,12 @@ declare_clippy_lint! { } pub struct IfThenSomeElseNone { - msrv: Option, + msrv: Msrv, } impl IfThenSomeElseNone { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -61,7 +59,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !meets_msrv(self.msrv, msrvs::BOOL_THEN) { + if !self.msrv.meets(msrvs::BOOL_THEN) { return; } @@ -94,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { } else { format!("{{ /* snippet */ {arg_snip} }}") }; - let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { + let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) { "then_some" } else { method_body.insert_str(0, "|| "); diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 0d5099bde6de..ee5f10da5b84 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -1,8 +1,9 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; use if_chain::if_chain; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; @@ -11,7 +12,6 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::Ident, Span}; @@ -51,14 +51,13 @@ declare_clippy_lint! { "avoid indexing on slices which could be destructed" } -#[derive(Copy, Clone)] pub struct IndexRefutableSlice { max_suggested_slice: u64, - msrv: Option, + msrv: Msrv, } impl IndexRefutableSlice { - pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option) -> Self { + pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self { Self { max_suggested_slice: max_suggested_slice_pattern_length, msrv, @@ -74,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr); if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id); - if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS); + if self.msrv.meets(msrvs::SLICE_PATTERNS); let found_slices = find_slice_values(cx, let_pat); if !found_slices.is_empty(); diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 60754b224fc8..dd1b23e7d9d2 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -1,13 +1,11 @@ -use clippy_utils::{ - diagnostics::{self, span_lint_and_sugg}, - meets_msrv, msrvs, source, - sugg::Sugg, - ty, -}; +use clippy_utils::diagnostics::{self, span_lint_and_sugg}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{source_map::Spanned, sym}; @@ -68,12 +66,12 @@ declare_clippy_lint! { } pub struct InstantSubtraction { - msrv: Option, + msrv: Msrv, } impl InstantSubtraction { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -101,7 +99,7 @@ impl LateLintPass<'_> for InstantSubtraction { } else { if_chain! { if !expr.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TRY_FROM); + if self.msrv.meets(msrvs::TRY_FROM); if is_an_instant(cx, lhs); if is_a_duration(cx, rhs); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3ab5031696d5..46acc9679f51 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -52,10 +52,9 @@ extern crate declare_clippy_lint; use std::io; use std::path::PathBuf; -use clippy_utils::parse_msrv; +use clippy_utils::msrvs::Msrv; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; -use rustc_semver::RustcVersion; use rustc_session::Session; #[cfg(feature = "internal")] @@ -322,48 +321,10 @@ pub use crate::utils::conf::{lookup_conf_file, Conf}; /// Used in `./src/driver.rs`. pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. + let msrv = Msrv::read(&conf.msrv, sess); + let msrv = move || msrv.clone(); - let msrv = conf.msrv.as_ref().and_then(|s| { - parse_msrv(s, None, None).or_else(|| { - sess.err(format!( - "error reading Clippy's configuration file. `{s}` is not a valid Rust version" - )); - None - }) - }); - - store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv })); -} - -fn read_msrv(conf: &Conf, sess: &Session) -> Option { - let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") - .ok() - .and_then(|v| parse_msrv(&v, None, None)); - let clippy_msrv = conf.msrv.as_ref().and_then(|s| { - parse_msrv(s, None, None).or_else(|| { - sess.err(format!( - "error reading Clippy's configuration file. `{s}` is not a valid Rust version" - )); - None - }) - }); - - if let Some(cargo_msrv) = cargo_msrv { - if let Some(clippy_msrv) = clippy_msrv { - // if both files have an msrv, let's compare them and emit a warning if they differ - if clippy_msrv != cargo_msrv { - sess.warn(format!( - "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" - )); - } - - Some(clippy_msrv) - } else { - Some(cargo_msrv) - } - } else { - clippy_msrv - } + store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() })); } #[doc(hidden)] @@ -595,43 +556,44 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); - let msrv = read_msrv(conf, sess); + let msrv = Msrv::read(&conf.msrv, sess); + let msrv = move || msrv.clone(); let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; let allow_expect_in_tests = conf.allow_expect_in_tests; let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; - store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv))); + store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, - msrv, + msrv(), allow_expect_in_tests, allow_unwrap_in_tests, )) }); - store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv))); + store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); let matches_for_let_else = conf.matches_for_let_else; - store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv))); - store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv))); - store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv))); - store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv))); - store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv))); - store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv))); - store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv))); - store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv))); - store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv(), matches_for_let_else))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv()))); + store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv()))); + store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv()))); + store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv()))); + store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv()))); + store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv()))); + store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv()))); + store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv()))); + store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv()))); store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)); - store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv))); - store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv))); + store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv()))); + store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv()))); store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length; store.register_late_pass(move |_| { Box::new(index_refutable_slice::IndexRefutableSlice::new( max_suggested_slice_pattern_length, - msrv, + msrv(), )) }); store.register_late_pass(|_| Box::::default()); @@ -648,7 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef)); store.register_late_pass(|_| Box::new(no_effect::NoEffect)); store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); - store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv))); + store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv()))); let cognitive_complexity_threshold = conf.cognitive_complexity_threshold; store.register_late_pass(move |_| { Box::new(cognitive_complexity::CognitiveComplexity::new( @@ -806,7 +768,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); - store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv))); + store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv()))); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex)); @@ -840,7 +802,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); - store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); + store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv()))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); store.register_late_pass(|_| Box::new(unused_async::UnusedAsync)); @@ -865,14 +827,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv))); + store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv()))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields)); store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); - store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv()))); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::::default()); let allow_dbg_in_tests = conf.allow_dbg_in_tests; @@ -896,20 +858,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); - store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); + store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv()))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)); store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)); store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); - store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv))); + store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv()))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); - store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv()))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); @@ -920,7 +882,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); - store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 6655c92b1da8..462d73cf0b97 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::get_parent_expr; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_parent_expr, meets_msrv, msrvs}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -34,12 +34,12 @@ declare_clippy_lint! { #[derive(Clone)] pub struct ManualBits { - msrv: Option, + msrv: Msrv, } impl ManualBits { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]); impl<'tcx> LateLintPass<'tcx> for ManualBits { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) { + if !self.msrv.meets(msrvs::MANUAL_BITS) { return; } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 02dc8755dd61..bb6d628af3b5 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -1,28 +1,25 @@ +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::higher::If; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::MaybePath; +use clippy_utils::{ + eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, +}; use itertools::Itertools; +use rustc_errors::Applicability; use rustc_errors::Diagnostic; use rustc_hir::{ def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span}; use std::ops::Deref; -use clippy_utils::{ - diagnostics::{span_lint_and_then, span_lint_hir_and_then}, - eq_expr_value, - higher::If, - is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, - sugg::Sugg, - ty::implements_trait, - visitors::is_const_evaluatable, - MaybePath, -}; -use rustc_errors::Applicability; - declare_clippy_lint! { /// ### What it does /// Identifies good opportunities for a clamp function from std or core, and suggests using it. @@ -87,11 +84,11 @@ declare_clippy_lint! { impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); pub struct ManualClamp { - msrv: Option, + msrv: Msrv, } impl ManualClamp { - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -114,7 +111,7 @@ struct InputMinMax<'tcx> { impl<'tcx> LateLintPass<'tcx> for ManualClamp { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !meets_msrv(self.msrv, msrvs::CLAMP) { + if !self.msrv.meets(msrvs::CLAMP) { return; } if !expr.span.from_expansion() { @@ -130,7 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if !meets_msrv(self.msrv, msrvs::CLAMP) { + if !self.msrv.meets(msrvs::CLAMP) { return; } for suggestion in is_two_if_pattern(cx, block) { diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index bb8c142f8e46..5ab049d8d133 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,15 +1,12 @@ +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, source::snippet}; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{def_id::DefId, sym}; -use clippy_utils::{ - diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet, -}; - declare_clippy_lint! { /// ### What it does /// Suggests to use dedicated built-in methods, @@ -45,12 +42,12 @@ declare_clippy_lint! { impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]); pub struct ManualIsAsciiCheck { - msrv: Option, + msrv: Msrv, } impl ManualIsAsciiCheck { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -70,11 +67,11 @@ enum CharRange { impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) { + if !self.msrv.meets(msrvs::IS_ASCII_DIGIT) { return; } - if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) { + if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) { return; } diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 1846596fa4c8..20f06830952c 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -1,16 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::peel_blocks; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{for_each_expr, Descend}; -use clippy_utils::{meets_msrv, msrvs, peel_blocks}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -50,13 +50,13 @@ declare_clippy_lint! { } pub struct ManualLetElse { - msrv: Option, + msrv: Msrv, matches_behaviour: MatchLintBehaviour, } impl ManualLetElse { #[must_use] - pub fn new(msrv: Option, matches_behaviour: MatchLintBehaviour) -> Self { + pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self { Self { msrv, matches_behaviour, @@ -69,7 +69,7 @@ impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]); impl<'tcx> LateLintPass<'tcx> for ManualLetElse { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { let if_let_or_match = if_chain! { - if meets_msrv(self.msrv, msrvs::LET_ELSE); + if self.msrv.meets(msrvs::LET_ELSE); if !in_external_macro(cx.sess(), stmt.span); if let StmtKind::Local(local) = stmt.kind; if let Some(init) = local.init; diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 6a42275322b4..d32fa1121f4e 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::is_doc_hidden; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; -use clippy_utils::{is_doc_hidden, meets_msrv, msrvs}; use rustc_ast::ast::{self, VisibilityKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -8,7 +9,6 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{self as hir, Expr, ExprKind, QPath}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::ty::DefIdTree; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; @@ -63,12 +63,12 @@ declare_clippy_lint! { #[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveStruct { - msrv: Option, + msrv: Msrv, } impl ManualNonExhaustiveStruct { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -77,14 +77,14 @@ impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); #[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveEnum { - msrv: Option, + msrv: Msrv, constructed_enum_variants: FxHashSet<(DefId, DefId)>, potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, } impl ManualNonExhaustiveEnum { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, constructed_enum_variants: FxHashSet::default(), @@ -97,7 +97,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustiveStruct { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { return; } @@ -149,7 +149,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { return; } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 6f25a2ed8e43..8d447c37150b 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,12 +1,12 @@ use clippy_utils::consts::{constant_full_int, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{in_constant, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -34,12 +34,12 @@ declare_clippy_lint! { } pub struct ManualRemEuclid { - msrv: Option, + msrv: Msrv, } impl ManualRemEuclid { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -48,11 +48,11 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]); impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::REM_EUCLID) { + if !self.msrv.meets(msrvs::REM_EUCLID) { return; } - if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) { + if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) { return; } diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 3181bc86d179..4907f02b5a3e 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq}; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -50,12 +50,12 @@ declare_clippy_lint! { } pub struct ManualRetain { - msrv: Option, + msrv: Msrv, } impl ManualRetain { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -71,9 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain { && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id) && match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) { - check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv); - check_iter(cx, parent_expr, left_expr, target_expr, self.msrv); - check_to_owned(cx, parent_expr, left_expr, target_expr, self.msrv); + check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); + check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); + check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv); } } @@ -85,7 +85,7 @@ fn check_into_iter( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) @@ -104,7 +104,7 @@ fn check_iter( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) @@ -127,9 +127,9 @@ fn check_to_owned( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { - if meets_msrv(msrv, msrvs::STRING_RETAIN) + if msrv.meets(msrvs::STRING_RETAIN) && let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) && match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD) @@ -215,10 +215,10 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo .any(|&method| match_def_path(cx, collect_def_id, method)) } -fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option) -> bool { +fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| { is_type_diagnostic_item(cx, expr_ty, *ty) - && acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv)) + && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv)) }) } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 0976940afac3..de166b9765f4 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,8 +1,9 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; -use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::{eq_expr_value, higher, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; @@ -11,7 +12,6 @@ use rustc_hir::BinOpKind; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; @@ -48,12 +48,12 @@ declare_clippy_lint! { } pub struct ManualStrip { - msrv: Option, + msrv: Msrv, } impl ManualStrip { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -68,7 +68,7 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { + if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) { return; } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 7d8171ead89e..7b15a307fecf 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -23,13 +23,13 @@ mod single_match; mod try_err; mod wild_in_or_pats; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_opt, walk_span_to_context}; -use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs}; +use clippy_utils::{higher, in_constant, is_span_match}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; @@ -930,13 +930,13 @@ declare_clippy_lint! { #[derive(Default)] pub struct Matches { - msrv: Option, + msrv: Msrv, infallible_destructuring_match_linted: bool, } impl Matches { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Matches::default() @@ -1000,9 +1000,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { - if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) - && match_like_matches::check_match(cx, expr, ex, arms)) - { + if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) { match_same_arms::check(cx, arms); } @@ -1034,7 +1032,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else); if !from_expansion { if let Some(else_expr) = if_let.if_else { - if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { + if self.msrv.meets(msrvs::MATCHES_MACRO) { match_like_matches::check_if_let( cx, expr, diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 0c4d9f100f7a..35024ec1224f 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res}; +use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; @@ -227,12 +227,12 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< } pub struct MemReplace { - msrv: Option, + msrv: Msrv, } impl MemReplace { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - if meets_msrv(self.msrv, msrvs::MEM_TAKE) { + if self.msrv.meets(msrvs::MEM_TAKE) { check_replace_with_default(cx, src, dest, expr.span); } } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index e9aeab2d5b62..4e6ec61f6a83 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,25 +1,25 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; -use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span}; use super::CLONED_INSTEAD_OF_COPIED; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: &Msrv) { let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let inner_ty = match recv_ty.kind() { // `Option` -> `T` ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) => + if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && msrv.meets(msrvs::OPTION_COPIED) => { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => { + _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(msrvs::ITERATOR_COPIED) => { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 720d9a68c85e..ae03da0d3f9c 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -1,27 +1,27 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::has_debug_impl; -use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span}; pub(super) fn check( cx: &LateContext<'_>, _expr: &rustc_hir::Expr<'_>, recv: &rustc_hir::Expr<'_>, - msrv: Option, expect_span: Span, err_span: Span, + msrv: &Msrv, ) { if_chain! { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); // Test the version to make sure the lint can be showed (expect_err has been // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) - if meets_msrv(msrv, msrvs::EXPECT_ERR); + if msrv.meets(msrvs::EXPECT_ERR); // Grabs the `Result` type let result_type = cx.typeck_results().expr_ty(recv); diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index ddf8a1f09b87..175e04f8ac06 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::is_trait_method; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; use rustc_span::sym; use super::FILTER_MAP_NEXT; @@ -14,10 +14,10 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { if is_trait_method(cx, expr, sym::Iterator) { - if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) { + if !msrv.meets(msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 304024e80666..301aff5ae6ac 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -1,23 +1,22 @@ //! Lint for `c.is_digit(10)` use super::IS_DIGIT_ASCII_RADIX; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::{ - consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs, - source::snippet_with_applicability, + consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability, }; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, self_arg: &'tcx Expr<'_>, radix: &'tcx Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { - if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) { + if !msrv.meets(msrvs::IS_ASCII_DIGIT) { return; } diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 6bc783c6d505..52cc1e0464bf 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; -use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks}; +use clippy_utils::{is_diag_trait_item, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -9,19 +10,12 @@ use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; -use rustc_semver::RustcVersion; use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; use super::MAP_CLONE; -pub(super) fn check( - cx: &LateContext<'_>, - e: &hir::Expr<'_>, - recv: &hir::Expr<'_>, - arg: &hir::Expr<'_>, - msrv: Option, -) { +pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) { if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id); if cx.tcx.impl_of_method(method_id) @@ -97,10 +91,10 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { ); } -fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option) { +fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) { let mut applicability = Applicability::MachineApplicable; - let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { + let (message, sugg_method) = if is_copy && msrv.meets(msrvs::ITERATOR_COPIED) { ("you are using an explicit closure for copying elements", "copied") } else { ("you are using an explicit closure for cloning elements", "cloned") diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 74fdead216b0..3122f72ee915 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::mutated_variables; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use super::MAP_UNWRAP_OR; @@ -19,13 +18,13 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, unwrap_arg: &'tcx hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) -> bool { // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) { + if is_result && !msrv.meets(msrvs::RESULT_MAP_OR_ELSE) { return false; } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index acb3b20a61b5..c5451195727c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -104,8 +104,9 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind}; @@ -113,7 +114,6 @@ use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; @@ -3163,7 +3163,7 @@ declare_clippy_lint! { pub struct Methods { avoid_breaking_exported_api: bool, - msrv: Option, + msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, } @@ -3172,7 +3172,7 @@ impl Methods { #[must_use] pub fn new( avoid_breaking_exported_api: bool, - msrv: Option, + msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, ) -> Self { @@ -3325,7 +3325,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, receiver, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -3501,7 +3501,7 @@ impl Methods { ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv), + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv), ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { @@ -3512,7 +3512,7 @@ impl Methods { map_collect_result_unit::check(cx, expr, m_recv, m_arg); }, Some(("take", take_self_arg, [take_arg], _, _)) => { - if meets_msrv(self.msrv, msrvs::STR_REPEAT) { + if self.msrv.meets(msrvs::STR_REPEAT) { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, @@ -3539,7 +3539,7 @@ impl Methods { }, ("expect", [_]) => match method_call(recv) { Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), + Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv), _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), }, ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests), @@ -3578,7 +3578,7 @@ impl Methods { unit_hash::check(cx, expr, recv, arg); }, ("is_file", []) => filetype_is_file::check(cx, expr, recv), - ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), + ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("iter" | "iter_mut" | "into_iter", []) => { @@ -3601,7 +3601,7 @@ impl Methods { }, (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { - map_clone::check(cx, expr, recv, m_arg, self.msrv); + map_clone::check(cx, expr, recv, m_arg, &self.msrv); if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) { iter_kv_map::check(cx, map_name, expr, recv2, m_arg); } @@ -3610,8 +3610,8 @@ impl Methods { } if let Some((name, recv2, args, span2,_)) = method_call(recv) { match (name, args) { - ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), - ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), + ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv), + ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv), ("filter", [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); }, @@ -3632,7 +3632,7 @@ impl Methods { match (name2, args2) { ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), + ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv), ("iter", []) => iter_next_slice::check(cx, expr, recv2), ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), ("skip_while", [_]) => skip_while_next::check(cx, expr), @@ -3680,10 +3680,10 @@ impl Methods { vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); }, ("seek", [arg]) => { - if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) { + if self.msrv.meets(msrvs::SEEK_FROM_CURRENT) { seek_from_current::check(cx, expr, recv, arg); } - if meets_msrv(self.msrv, msrvs::SEEK_REWIND) { + if self.msrv.meets(msrvs::SEEK_REWIND) { seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span); } }, @@ -3699,7 +3699,7 @@ impl Methods { ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); - str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv); + str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { @@ -3717,7 +3717,7 @@ impl Methods { }, ("take", []) => needless_option_take::check(cx, expr, recv), ("then", [arg]) => { - if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { + if !self.msrv.meets(msrvs::BOOL_THEN_SOME) { return; } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); @@ -3760,7 +3760,7 @@ impl Methods { }, ("unwrap_or_else", [u_arg]) => match method_call(recv) { Some(("map", recv, [map_arg], _, _)) - if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, + if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index e6eb64bcbde6..3e33f9193374 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks}; +use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::sym; use super::OPTION_AS_REF_DEREF; @@ -19,9 +19,9 @@ pub(super) fn check( as_ref_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, is_mut: bool, - msrv: Option, + msrv: &Msrv, ) { - if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) { + if !msrv.meets(msrvs::OPTION_AS_DEREF) { return; } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 1acac59144ce..3c01ce1fecd3 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,9 +1,10 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; -use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; @@ -12,7 +13,6 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span, Symbol, SyntaxContext}; use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; @@ -24,7 +24,7 @@ pub(super) fn check( self_arg: &Expr<'_>, pat_arg: &Expr<'_>, count: u128, - msrv: Option, + msrv: &Msrv, ) { if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; @@ -34,7 +34,7 @@ pub(super) fn check( IterUsageKind::Nth(n) => count > n + 1, IterUsageKind::NextTuple => count > 2, }; - let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE); + let manual = count == 2 && msrv.meets(msrvs::STR_SPLIT_ONCE); match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 4b4f2f47b1d9..21d1df4d0c21 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -1,11 +1,11 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; use rustc_hir_analysis::check::{FnCtxt, Inherited}; @@ -16,7 +16,6 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::EarlyBinder; use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; -use rustc_semver::RustcVersion; use rustc_span::{sym, Symbol}; use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; use std::cmp::max; @@ -29,7 +28,7 @@ pub fn check<'tcx>( method_name: Symbol, receiver: &'tcx Expr<'_>, args: &'tcx [Expr<'_>], - msrv: Option, + msrv: &Msrv, ) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); @@ -200,7 +199,7 @@ fn check_into_iter_call_arg( expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>, - msrv: Option, + msrv: &Msrv, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -215,7 +214,7 @@ fn check_into_iter_call_arg( if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { + let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 71cc0d0a81cd..5bc04bc17fb4 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::ty::has_drop; -use clippy_utils::{ - fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method, -}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; @@ -11,7 +10,6 @@ use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -75,12 +73,12 @@ declare_clippy_lint! { impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); pub struct MissingConstForFn { - msrv: Option, + msrv: Msrv, } impl MissingConstForFn { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -95,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { - if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) { + if !self.msrv.meets(msrvs::CONST_IF_MATCH) { return; } @@ -152,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { cx.tcx.sess.span_err(span, err.as_ref()); } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index c6fbb5e805ab..0a1b9d173cf9 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,16 +1,16 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, path_to_local}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use std::cmp::Ordering; @@ -161,12 +161,12 @@ declare_clippy_lint! { } pub struct Ranges { - msrv: Option, + msrv: Msrv, } impl Ranges { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -181,7 +181,7 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, l, r) = expr.kind { - if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { + if self.msrv.meets(msrvs::RANGE_CONTAINS) { check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } } diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 40b03068f6c7..61bff4a0e38d 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -37,12 +36,12 @@ declare_clippy_lint! { } pub struct RedundantFieldNames { - msrv: Option, + msrv: Msrv, } impl RedundantFieldNames { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -51,7 +50,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) { + if !self.msrv.meets(msrvs::FIELD_INIT_SHORTHAND) { return; } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 60ba62c4a433..3aa2490bc44e 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; -use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -34,12 +33,12 @@ declare_clippy_lint! { } pub struct RedundantStaticLifetimes { - msrv: Option, + msrv: Msrv, } impl RedundantStaticLifetimes { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -96,7 +95,7 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) { + if !self.msrv.meets(msrvs::STATIC_IN_CONST) { return; } diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 424a6e9264e4..83e651aba8e8 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -16,10 +16,10 @@ mod utils; mod wrong_transmute; use clippy_utils::in_constant; +use clippy_utils::msrvs::Msrv; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; @@ -410,7 +410,7 @@ declare_clippy_lint! { } pub struct Transmute { - msrv: Option, + msrv: Msrv, } impl_lint_pass!(Transmute => [ CROSSPOINTER_TRANSMUTE, @@ -431,7 +431,7 @@ impl_lint_pass!(Transmute => [ ]); impl Transmute { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -461,7 +461,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmuting_null::check(cx, e, arg, to_ty) - | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) + | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 12d0b866e1c9..3dde4eee6717 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -1,12 +1,12 @@ use super::TRANSMUTE_PTR_TO_REF; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{meets_msrv, msrvs, sugg}; +use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitable}; -use rustc_semver::RustcVersion; /// Checks for `transmute_ptr_to_ref` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>( to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, path: &'tcx Path<'_>, - msrv: Option, + msrv: &Msrv, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => { @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); - if meets_msrv(msrv, msrvs::POINTER_CAST) { + if msrv.meets(msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() @@ -46,7 +46,7 @@ pub(super) fn check<'tcx>( } } else if from_ptr_ty.ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { - if meets_msrv(msrv, msrvs::POINTER_CAST) { + if msrv.meets(msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index b305dae76084..2c2730267db6 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -2,14 +2,14 @@ use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{meets_msrv, msrvs, over}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::over; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::DUMMY_SP; @@ -45,14 +45,13 @@ declare_clippy_lint! { "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" } -#[derive(Clone, Copy)] pub struct UnnestedOrPatterns { - msrv: Option, + msrv: Msrv, } impl UnnestedOrPatterns { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -61,13 +60,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &a.pat); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { if let ast::ExprKind::Let(pat, _, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } @@ -75,13 +74,13 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &p.pat); } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &l.pat); } } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 464ffdc0a2ad..06975443d8fa 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::same_type_and_consts; -use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -14,7 +15,6 @@ use rustc_hir::{ }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -57,13 +57,13 @@ declare_clippy_lint! { #[derive(Default)] pub struct UseSelf { - msrv: Option, + msrv: Msrv, stack: Vec, } impl UseSelf { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Self::default() @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { if_chain! { if !hir_ty.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, in_body, @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if !expr.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id); then {} else { return; } @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { if_chain! { if !pat.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); // get the path from the pattern if let PatKind::Path(QPath::Resolved(_, path)) diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index 1e994e3f2b17..9876a8a765cc 100644 --- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -41,7 +41,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .type_of(f.did) .walk() .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) + .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV)) }); if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); then { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b0063758dcef..86ee172cb28a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -105,8 +105,6 @@ use rustc_middle::ty::{ layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture, }; use rustc_middle::ty::{FloatTy, IntTy, UintTy}; -use rustc_semver::RustcVersion; -use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::sym; @@ -118,36 +116,17 @@ use crate::consts::{constant, Constant}; use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param}; use crate::visitors::for_each_expr; -pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { - if let Ok(version) = RustcVersion::parse(msrv) { - return Some(version); - } else if let Some(sess) = sess { - if let Some(span) = span { - sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); - } - } - None -} - -pub fn meets_msrv(msrv: Option, lint_msrv: RustcVersion) -> bool { - msrv.map_or(true, |msrv| msrv.meets(lint_msrv)) -} - #[macro_export] macro_rules! extract_msrv_attr { ($context:ident) => { fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { let sess = rustc_lint::LintContext::sess(cx); - match $crate::get_unique_inner_attr(sess, attrs, "msrv") { - Some(msrv_attr) => { - if let Some(msrv) = msrv_attr.value_str() { - self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span)); - } else { - sess.span_err(msrv_attr.span, "bad clippy attribute"); - } - }, - _ => (), - } + self.msrv.enter_lint_attrs(sess, attrs); + } + + fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { + let sess = rustc_lint::LintContext::sess(cx); + self.msrv.exit_lint_attrs(sess, attrs); } }; } diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 79b19e6fb3eb..2c9f75736e52 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -1,4 +1,11 @@ +use std::sync::OnceLock; + +use rustc_ast::Attribute; use rustc_semver::RustcVersion; +use rustc_session::Session; +use rustc_span::Span; + +use crate::attrs::get_unique_inner_attr; macro_rules! msrv_aliases { ($($major:literal,$minor:literal,$patch:literal { @@ -40,3 +47,97 @@ msrv_aliases! { 1,16,0 { STR_REPEAT } 1,55,0 { SEEK_REWIND } } + +fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { + if let Ok(version) = RustcVersion::parse(msrv) { + return Some(version); + } else if let Some(sess) = sess { + if let Some(span) = span { + sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); + } + } + None +} + +/// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]` +#[derive(Debug, Clone, Default)] +pub struct Msrv { + stack: Vec, +} + +impl Msrv { + fn new(initial: Option) -> Self { + Self { + stack: Vec::from_iter(initial), + } + } + + fn read_inner(conf_msrv: &Option, sess: &Session) -> Self { + let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") + .ok() + .and_then(|v| parse_msrv(&v, None, None)); + let clippy_msrv = conf_msrv.as_ref().and_then(|s| { + parse_msrv(s, None, None).or_else(|| { + sess.err(format!( + "error reading Clippy's configuration file. `{s}` is not a valid Rust version" + )); + None + }) + }); + + // if both files have an msrv, let's compare them and emit a warning if they differ + if let Some(cargo_msrv) = cargo_msrv + && let Some(clippy_msrv) = clippy_msrv + && clippy_msrv != cargo_msrv + { + sess.warn(format!( + "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" + )); + } + + Self::new(clippy_msrv.or(cargo_msrv)) + } + + /// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version` + /// field in `Cargo.toml` + /// + /// Returns a `&'static Msrv` as `Copy` types are more easily passed to the + /// `register_{late,early}_pass` callbacks + pub fn read(conf_msrv: &Option, sess: &Session) -> &'static Self { + static PARSED: OnceLock = OnceLock::new(); + + PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess)) + } + + pub fn current(&self) -> Option { + self.stack.last().copied() + } + + pub fn meets(&self, required: RustcVersion) -> bool { + self.current().map_or(true, |version| version.meets(required)) + } + + fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option { + if let Some(msrv_attr) = get_unique_inner_attr(sess, attrs, "msrv") { + if let Some(msrv) = msrv_attr.value_str() { + return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span)); + } + + sess.span_err(msrv_attr.span, "bad clippy attribute"); + } + + None + } + + pub fn enter_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) { + if let Some(version) = Self::parse_attr(sess, attrs) { + self.stack.push(version); + } + } + + pub fn exit_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) { + if Self::parse_attr(sess, attrs).is_some() { + self.stack.pop(); + } + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 6c09c146082a..3cf8cb76651e 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -60,6 +60,8 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; #[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"]; +#[cfg(feature = "internal")] +pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; @@ -101,8 +103,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; -#[cfg(feature = "internal")] -pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 65722f142aa6..0def12456156 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -3,6 +3,7 @@ // of terminologies might not be relevant in the context of Clippy. Note that its behavior might // differ from the time of `rustc` even if the name stays the same. +use crate::msrvs::Msrv; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::{ @@ -18,7 +19,7 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option) -> McfResult { +pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { @@ -280,7 +281,7 @@ fn check_terminator<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, terminator: &Terminator<'tcx>, - msrv: Option, + msrv: &Msrv, ) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { @@ -364,7 +365,7 @@ fn check_terminator<'tcx>( } } -fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bool { +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { tcx.is_const_fn(def_id) && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level { @@ -383,15 +384,12 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bo let since = rustc_span::Symbol::intern(short_version); - crate::meets_msrv( - msrv, - RustcVersion::parse(since.as_str()).unwrap_or_else(|err| { - panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}") - }), - ) + msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| { + panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}") + })) } else { // Unstable const fn with the feature enabled. - msrv.is_none() + msrv.current().is_none() } }) } diff --git a/tests/ui-internal/invalid_msrv_attr_impl.fixed b/tests/ui-internal/invalid_msrv_attr_impl.fixed index 900a8fffd408..08634063a575 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.fixed +++ b/tests/ui-internal/invalid_msrv_attr_impl.fixed @@ -11,9 +11,9 @@ extern crate rustc_middle; #[macro_use] extern crate rustc_session; use clippy_utils::extract_msrv_attr; +use clippy_utils::msrvs::Msrv; use rustc_hir::Expr; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_semver::RustcVersion; declare_lint! { pub TEST_LINT, @@ -22,7 +22,7 @@ declare_lint! { } struct Pass { - msrv: Option, + msrv: Msrv, } impl_lint_pass!(Pass => [TEST_LINT]); diff --git a/tests/ui-internal/invalid_msrv_attr_impl.rs b/tests/ui-internal/invalid_msrv_attr_impl.rs index 4bc8164db67b..f8af77e6d395 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.rs +++ b/tests/ui-internal/invalid_msrv_attr_impl.rs @@ -11,9 +11,9 @@ extern crate rustc_middle; #[macro_use] extern crate rustc_session; use clippy_utils::extract_msrv_attr; +use clippy_utils::msrvs::Msrv; use rustc_hir::Expr; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_semver::RustcVersion; declare_lint! { pub TEST_LINT, @@ -22,7 +22,7 @@ declare_lint! { } struct Pass { - msrv: Option, + msrv: Msrv, } impl_lint_pass!(Pass => [TEST_LINT]); diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index cd148063bf06..28ab132394b8 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -27,3 +27,26 @@ fn no_patch_meets() { #![clippy::msrv = "1.43"] let log2_10 = 3.321928094887362; } + +// https://github.com/rust-lang/rust-clippy/issues/6920 +fn scoping() { + mod m { + #![clippy::msrv = "1.42.0"] + } + + // Should warn + let log2_10 = 3.321928094887362; + + mod a { + #![clippy::msrv = "1.42.0"] + + fn should_warn() { + #![clippy::msrv = "1.43.0"] + let log2_10 = 3.321928094887362; + } + + fn should_not_warn() { + let log2_10 = 3.321928094887362; + } + } +} diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 68aa58748190..6174443372f8 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -23,5 +23,21 @@ LL | let log2_10 = 3.321928094887362; | = help: consider using the constant directly -error: aborting due to 3 previous errors +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:38:19 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:45:27 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly + +error: aborting due to 5 previous errors From 25e98bf708609d536ae53880c8f297917820f37e Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 20:02:06 +0100 Subject: [PATCH 228/524] Bump Clippy version -> 0.1.67 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5223dca073fd..6bdac84ada00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.66" +version = "0.1.67" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 5368932b3528..dcadd012a44d 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.66" +version = "0.1.67" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 83fee7bb39c2..fb9f4740ecc5 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.66" +version = "0.1.67" edition = "2021" publish = false diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 68bb0be67a75..578109840fb7 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.66" +version = "0.1.67" edition = "2021" publish = false From 661f13ce3b34dd2eb5bd0422ab464d41b725fc47 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 20:02:14 +0100 Subject: [PATCH 229/524] Bump nightly version -> 2022-11-21 --- rust-toolchain | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust-toolchain b/rust-toolchain index 748d8a317160..a806c1564796 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-10-20" -components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] +channel = "nightly-2022-11-21" +components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 5907e9155ed7f1312d108aa2110853472da3b029 Mon Sep 17 00:00:00 2001 From: ozkanonur Date: Sun, 20 Nov 2022 02:40:31 +0300 Subject: [PATCH 230/524] pass clippy sysroot env if given r=ozkanonur Signed-off-by: ozkanonur --- src/driver.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index 51ae27fbbd7f..ee2a3ad20d3e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -252,6 +252,13 @@ pub fn main() { exit(rustc_driver::catch_with_exit_code(move || { let mut orig_args: Vec = env::args().collect(); + let sys_root_env = std::env::var("SYSROOT").ok(); + let pass_sysroot_env_if_given = |args: &mut Vec, sys_root_env| { + if let Some(sys_root) = sys_root_env { + args.extend(vec!["--sysroot".into(), sys_root]); + }; + }; + // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc" // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver // uses @@ -259,7 +266,10 @@ pub fn main() { orig_args.remove(pos); orig_args[0] = "rustc".to_string(); - return rustc_driver::RunCompiler::new(&orig_args, &mut DefaultCallbacks).run(); + let mut args: Vec = orig_args.clone(); + pass_sysroot_env_if_given(&mut args, sys_root_env); + + return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run(); } if orig_args.iter().any(|a| a == "--version" || a == "-V") { @@ -282,6 +292,9 @@ pub fn main() { exit(0); } + let mut args: Vec = orig_args.clone(); + pass_sysroot_env_if_given(&mut args, sys_root_env); + let mut no_deps = false; let clippy_args_var = env::var("CLIPPY_ARGS").ok(); let clippy_args = clippy_args_var @@ -310,11 +323,10 @@ pub fn main() { let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package); if clippy_enabled { - let mut args: Vec = orig_args.clone(); args.extend(clippy_args); rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run() } else { - rustc_driver::RunCompiler::new(&orig_args, &mut RustcCallbacks { clippy_args_var }).run() + rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run() } })) } From 05b914a92b9385066c756c9fc59822545e0fbe0a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 20:15:50 +0100 Subject: [PATCH 231/524] Fix custom ICE message test on windows --- tests/ui-internal/custom_ice_message.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs index 5057a018300e..4be04f77f5bd 100644 --- a/tests/ui-internal/custom_ice_message.rs +++ b/tests/ui-internal/custom_ice_message.rs @@ -2,6 +2,7 @@ // normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" // normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" // normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" +// normalize-stderr-test: "'rustc'" -> "''" #![deny(clippy::internal)] #![allow(clippy::missing_clippy_version_attribute)] From ae8f75c6a3fe019f2f04145049aba84adf4f3f94 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 25 Oct 2022 20:15:15 +0400 Subject: [PATCH 232/524] Unreserve braced enum variants in value namespace --- clippy_lints/src/matches/match_wild_enum.rs | 14 +++++++------- .../utils/internal_lints/unnecessary_def_path.rs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 6f8d766aef7c..7f8d124838cb 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -65,14 +65,14 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { _ => return, }; if arm.guard.is_none() { - missing_variants.retain(|e| e.ctor_def_id != Some(id)); + missing_variants.retain(|e| e.ctor_def_id() != Some(id)); } path }, PatKind::TupleStruct(path, patterns, ..) => { if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { - missing_variants.retain(|e| e.ctor_def_id != Some(id)); + missing_variants.retain(|e| e.ctor_def_id() != Some(id)); } } path @@ -122,11 +122,11 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { s }, variant.name, - match variant.ctor_kind { - CtorKind::Fn if variant.fields.len() == 1 => "(_)", - CtorKind::Fn => "(..)", - CtorKind::Const => "", - CtorKind::Fictive => "{ .. }", + match variant.ctor_kind() { + Some(CtorKind::Fn) if variant.fields.len() == 1 => "(_)", + Some(CtorKind::Fn) => "(..)", + Some(CtorKind::Const) => "", + None => "{ .. }", } ) }; diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index cfba7fa8791d..f2276395fed0 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -133,11 +133,11 @@ impl UnnecessaryDefPath { let has_ctor = match cx.tcx.def_kind(def_id) { DefKind::Struct => { let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) }, DefKind::Variant => { let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) }, _ => false, }; From 46c5a5d234f13dcf4bb4cf4241b2addedbf0be14 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 20:34:47 +0100 Subject: [PATCH 233/524] Merge commit 'f4850f7292efa33759b4f7f9b7621268979e9914' into clippyup --- CHANGELOG.md | 159 ++++- CODE_OF_CONDUCT.md | 69 +- CONTRIBUTING.md | 33 + Cargo.toml | 2 +- README.md | 8 +- clippy_dev/Cargo.toml | 1 - clippy_dev/src/lint.rs | 8 - clippy_dev/src/update_lints.rs | 307 +++------ clippy_lints/Cargo.toml | 3 +- clippy_lints/src/attrs.rs | 37 +- clippy_lints/src/await_holding_invalid.rs | 3 +- clippy_lints/src/bool_to_int_with_if.rs | 27 +- clippy_lints/src/booleans.rs | 2 +- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/cognitive_complexity.rs | 21 +- clippy_lints/src/declared_lints.rs | 628 ++++++++++++++++++ clippy_lints/src/dereference.rs | 128 +++- clippy_lints/src/disallowed_macros.rs | 5 +- clippy_lints/src/disallowed_methods.rs | 5 +- clippy_lints/src/disallowed_types.rs | 40 +- clippy_lints/src/doc.rs | 123 ++-- clippy_lints/src/enum_variants.rs | 2 +- clippy_lints/src/equatable_if_let.rs | 27 +- clippy_lints/src/escape.rs | 8 +- clippy_lints/src/excessive_bools.rs | 129 ++-- clippy_lints/src/fallible_impl_from.rs | 2 +- clippy_lints/src/from_raw_with_void_ptr.rs | 77 +++ clippy_lints/src/functions/mod.rs | 2 +- clippy_lints/src/functions/must_use.rs | 30 +- clippy_lints/src/functions/result.rs | 68 +- clippy_lints/src/implicit_hasher.rs | 4 +- clippy_lints/src/index_refutable_slice.rs | 4 +- clippy_lints/src/indexing_slicing.rs | 6 +- clippy_lints/src/instant_subtraction.rs | 184 +++++ clippy_lints/src/int_plus_one.rs | 28 +- .../src/invalid_upcast_comparisons.rs | 2 +- clippy_lints/src/large_enum_variant.rs | 60 +- clippy_lints/src/len_zero.rs | 3 +- clippy_lints/src/let_underscore.rs | 157 ++--- clippy_lints/src/lib.register_all.rs | 368 ---------- clippy_lints/src/lib.register_cargo.rs | 11 - clippy_lints/src/lib.register_complexity.rs | 111 ---- clippy_lints/src/lib.register_correctness.rs | 78 --- clippy_lints/src/lib.register_internal.rs | 22 - clippy_lints/src/lib.register_lints.rs | 620 ----------------- clippy_lints/src/lib.register_nursery.rs | 39 -- clippy_lints/src/lib.register_pedantic.rs | 104 --- clippy_lints/src/lib.register_perf.rs | 34 - clippy_lints/src/lib.register_restriction.rs | 90 --- clippy_lints/src/lib.register_style.rs | 131 ---- clippy_lints/src/lib.register_suspicious.rs | 38 -- clippy_lints/src/lib.rs | 267 ++++---- clippy_lints/src/lifetimes.rs | 131 ++-- clippy_lints/src/loops/mod.rs | 26 - clippy_lints/src/loops/mut_range_bound.rs | 12 +- clippy_lints/src/loops/never_loop.rs | 114 ++-- clippy_lints/src/manual_instant_elapsed.rs | 69 -- clippy_lints/src/manual_is_ascii_check.rs | 158 +++++ clippy_lints/src/manual_let_else.rs | 297 +++++++++ clippy_lints/src/map_unit_fn.rs | 2 +- .../matches/infallible_destructuring_match.rs | 7 +- clippy_lints/src/matches/manual_filter.rs | 2 +- .../matches/significant_drop_in_scrutinee.rs | 8 +- clippy_lints/src/matches/single_match.rs | 2 +- .../src/methods/chars_cmp_with_unwrap.rs | 4 +- clippy_lints/src/methods/chars_last_cmp.rs | 2 +- .../src/methods/chars_last_cmp_with_unwrap.rs | 2 +- clippy_lints/src/methods/chars_next_cmp.rs | 2 +- .../src/methods/chars_next_cmp_with_unwrap.rs | 2 +- .../src/methods/collapsible_str_replace.rs | 6 +- clippy_lints/src/methods/expect_used.rs | 10 +- clippy_lints/src/methods/filter_map.rs | 8 +- .../src/methods/inefficient_to_string.rs | 4 +- clippy_lints/src/methods/iter_nth_zero.rs | 2 +- .../iter_on_single_or_empty_collections.rs | 2 +- .../methods/manual_saturating_arithmetic.rs | 2 +- clippy_lints/src/methods/manual_str_repeat.rs | 2 - clippy_lints/src/methods/map_clone.rs | 4 +- .../src/methods/map_collect_result_unit.rs | 15 +- clippy_lints/src/methods/map_err_ignore.rs | 2 +- clippy_lints/src/methods/mod.rs | 281 +++++--- .../{loops => methods}/needless_collect.rs | 223 ++++--- .../src/methods/option_as_ref_deref.rs | 4 +- clippy_lints/src/methods/or_fun_call.rs | 57 +- clippy_lints/src/methods/seek_from_current.rs | 48 ++ .../seek_to_start_instead_of_rewind.rs | 45 ++ .../src/methods/string_extend_chars.rs | 6 +- clippy_lints/src/methods/suspicious_map.rs | 2 +- clippy_lints/src/methods/unnecessary_join.rs | 2 +- clippy_lints/src/methods/unwrap_used.rs | 10 +- .../src/missing_enforced_import_rename.rs | 2 +- .../src/mixed_read_write_in_expression.rs | 4 +- clippy_lints/src/mut_key.rs | 155 +++-- clippy_lints/src/mut_mut.rs | 16 +- clippy_lints/src/needless_borrowed_ref.rs | 108 +-- clippy_lints/src/needless_continue.rs | 8 +- clippy_lints/src/needless_pass_by_value.rs | 8 +- clippy_lints/src/octal_escapes.rs | 4 +- .../src/operators/arithmetic_side_effects.rs | 110 +-- clippy_lints/src/operators/op_ref.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 13 +- clippy_lints/src/partialeq_to_none.rs | 2 +- clippy_lints/src/pattern_type_mismatch.rs | 4 +- clippy_lints/src/ptr_offset_with_cast.rs | 8 +- clippy_lints/src/question_mark.rs | 1 + clippy_lints/src/redundant_closure_call.rs | 4 +- clippy_lints/src/redundant_pub_crate.rs | 3 +- clippy_lints/src/renamed_lints.rs | 5 +- .../src/single_component_path_imports.rs | 219 +++--- .../src/slow_vector_initialization.rs | 2 +- .../src/suspicious_xor_used_as_pow.rs | 53 ++ clippy_lints/src/swap.rs | 8 +- clippy_lints/src/trailing_empty_array.rs | 8 +- .../src/transmute/transmute_undefined_repr.rs | 164 ++--- clippy_lints/src/transmute/utils.rs | 11 +- clippy_lints/src/types/box_collection.rs | 19 +- clippy_lints/src/types/mod.rs | 4 +- .../src/types/redundant_allocation.rs | 24 +- clippy_lints/src/types/vec_box.rs | 2 +- .../src/undocumented_unsafe_blocks.rs | 40 +- clippy_lints/src/unsafe_removed_from_name.rs | 2 +- clippy_lints/src/unused_peekable.rs | 2 +- clippy_lints/src/unused_rounding.rs | 11 +- clippy_lints/src/unused_unit.rs | 8 +- clippy_lints/src/use_self.rs | 39 +- clippy_lints/src/utils/conf.rs | 31 +- .../interning_defined_symbol.rs | 4 +- .../src/utils/internal_lints/invalid_paths.rs | 4 +- .../internal_lints/lint_without_lint_pass.rs | 2 +- .../internal_lints/unnecessary_def_path.rs | 45 +- clippy_lints/src/write.rs | 17 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/src/ast_utils.rs | 20 +- clippy_utils/src/consts.rs | 32 +- clippy_utils/src/diagnostics.rs | 12 +- clippy_utils/src/hir_utils.rs | 33 +- clippy_utils/src/lib.rs | 315 +++++---- clippy_utils/src/msrvs.rs | 6 +- clippy_utils/src/paths.rs | 3 + clippy_utils/src/qualify_min_const_fn.rs | 6 +- clippy_utils/src/source.rs | 22 +- clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/ty.rs | 224 ++++++- clippy_utils/src/usage.rs | 7 +- declare_clippy_lint/Cargo.toml | 13 + declare_clippy_lint/src/lib.rs | 173 +++++ lintcheck/src/main.rs | 47 +- rust-toolchain | 4 +- src/docs.rs | 606 ----------------- src/docs/absurd_extreme_comparisons.txt | 25 - src/docs/alloc_instead_of_core.txt | 18 - src/docs/allow_attributes_without_reason.txt | 22 - src/docs/almost_complete_letter_range.txt | 15 - src/docs/almost_swapped.txt | 15 - src/docs/approx_constant.txt | 24 - src/docs/arithmetic_side_effects.txt | 33 - src/docs/as_conversions.txt | 32 - src/docs/as_ptr_cast_mut.txt | 19 - src/docs/as_underscore.txt | 21 - src/docs/assertions_on_constants.txt | 14 - src/docs/assertions_on_result_states.txt | 14 - src/docs/assign_op_pattern.txt | 28 - src/docs/async_yields_async.txt | 28 - src/docs/await_holding_invalid_type.txt | 29 - src/docs/await_holding_lock.txt | 51 -- src/docs/await_holding_refcell_ref.txt | 47 -- src/docs/bad_bit_mask.txt | 30 - src/docs/bind_instead_of_map.txt | 22 - src/docs/blanket_clippy_restriction_lints.txt | 16 - src/docs/blocks_in_if_conditions.txt | 21 - src/docs/bool_assert_comparison.txt | 16 - src/docs/bool_comparison.txt | 18 - src/docs/bool_to_int_with_if.txt | 26 - src/docs/borrow_as_ptr.txt | 26 - src/docs/borrow_deref_ref.txt | 27 - src/docs/borrow_interior_mutable_const.txt | 40 -- src/docs/borrowed_box.txt | 19 - src/docs/box_collection.txt | 23 - src/docs/box_default.txt | 17 - src/docs/boxed_local.txt | 18 - src/docs/branches_sharing_code.txt | 32 - src/docs/builtin_type_shadow.txt | 15 - src/docs/bytes_count_to_len.txt | 18 - src/docs/bytes_nth.txt | 16 - src/docs/cargo_common_metadata.txt | 33 - ...e_sensitive_file_extension_comparisons.txt | 21 - src/docs/cast_abs_to_unsigned.txt | 16 - src/docs/cast_enum_constructor.txt | 11 - src/docs/cast_enum_truncation.txt | 12 - src/docs/cast_lossless.txt | 26 - src/docs/cast_nan_to_int.txt | 15 - src/docs/cast_possible_truncation.txt | 16 - src/docs/cast_possible_wrap.txt | 17 - src/docs/cast_precision_loss.txt | 19 - src/docs/cast_ptr_alignment.txt | 21 - src/docs/cast_ref_to_mut.txt | 28 - src/docs/cast_sign_loss.txt | 15 - src/docs/cast_slice_different_sizes.txt | 38 -- src/docs/cast_slice_from_raw_parts.txt | 20 - src/docs/char_lit_as_u8.txt | 21 - src/docs/chars_last_cmp.txt | 17 - src/docs/chars_next_cmp.txt | 19 - src/docs/checked_conversions.txt | 15 - src/docs/clone_double_ref.txt | 16 - src/docs/clone_on_copy.txt | 11 - src/docs/clone_on_ref_ptr.txt | 21 - src/docs/cloned_instead_of_copied.txt | 16 - src/docs/cmp_nan.txt | 16 - src/docs/cmp_null.txt | 23 - src/docs/cmp_owned.txt | 18 - src/docs/cognitive_complexity.txt | 13 - src/docs/collapsible_else_if.txt | 29 - src/docs/collapsible_if.txt | 23 - src/docs/collapsible_match.txt | 31 - src/docs/collapsible_str_replace.txt | 19 - src/docs/comparison_chain.txt | 36 - src/docs/comparison_to_empty.txt | 31 - src/docs/copy_iterator.txt | 20 - src/docs/crate_in_macro_def.txt | 35 - src/docs/create_dir.txt | 15 - src/docs/crosspointer_transmute.txt | 12 - src/docs/dbg_macro.txt | 16 - src/docs/debug_assert_with_mut_call.txt | 18 - src/docs/decimal_literal_representation.txt | 13 - src/docs/declare_interior_mutable_const.txt | 46 -- src/docs/default_instead_of_iter_empty.txt | 15 - src/docs/default_numeric_fallback.txt | 28 - src/docs/default_trait_access.txt | 16 - src/docs/default_union_representation.txt | 36 - src/docs/deprecated_cfg_attr.txt | 24 - src/docs/deprecated_semver.txt | 13 - src/docs/deref_addrof.txt | 22 - src/docs/deref_by_slicing.txt | 17 - src/docs/derivable_impls.txt | 35 - src/docs/derive_hash_xor_eq.txt | 23 - src/docs/derive_ord_xor_partial_ord.txt | 44 -- src/docs/derive_partial_eq_without_eq.txt | 25 - src/docs/disallowed_macros.txt | 36 - src/docs/disallowed_methods.txt | 41 -- src/docs/disallowed_names.txt | 12 - src/docs/disallowed_script_idents.txt | 32 - src/docs/disallowed_types.txt | 33 - src/docs/diverging_sub_expression.txt | 19 - src/docs/doc_link_with_quotes.txt | 16 - src/docs/doc_markdown.txt | 35 - src/docs/double_comparisons.txt | 17 - src/docs/double_must_use.txt | 17 - src/docs/double_neg.txt | 12 - src/docs/double_parens.txt | 24 - src/docs/drop_copy.txt | 15 - src/docs/drop_non_drop.txt | 13 - src/docs/drop_ref.txt | 17 - src/docs/duplicate_mod.txt | 31 - src/docs/duplicate_underscore_argument.txt | 16 - src/docs/duration_subsec.txt | 19 - src/docs/else_if_without_else.txt | 27 - src/docs/empty_drop.txt | 20 - src/docs/empty_enum.txt | 27 - src/docs/empty_line_after_outer_attr.txt | 35 - src/docs/empty_loop.txt | 27 - src/docs/empty_structs_with_brackets.txt | 14 - src/docs/enum_clike_unportable_variant.txt | 16 - src/docs/enum_glob_use.txt | 24 - src/docs/enum_variant_names.txt | 30 - src/docs/eq_op.txt | 22 - src/docs/equatable_if_let.txt | 23 - src/docs/erasing_op.txt | 15 - src/docs/err_expect.txt | 16 - src/docs/excessive_precision.txt | 18 - src/docs/exhaustive_enums.txt | 23 - src/docs/exhaustive_structs.txt | 23 - src/docs/exit.txt | 12 - src/docs/expect_fun_call.txt | 24 - src/docs/expect_used.txt | 26 - src/docs/expl_impl_clone_on_copy.txt | 20 - src/docs/explicit_auto_deref.txt | 16 - src/docs/explicit_counter_loop.txt | 21 - src/docs/explicit_deref_methods.txt | 24 - src/docs/explicit_into_iter_loop.txt | 20 - src/docs/explicit_iter_loop.txt | 25 - src/docs/explicit_write.txt | 18 - src/docs/extend_with_drain.txt | 21 - src/docs/extra_unused_lifetimes.txt | 23 - src/docs/fallible_impl_from.txt | 32 - src/docs/field_reassign_with_default.txt | 23 - src/docs/filetype_is_file.txt | 29 - src/docs/filter_map_identity.txt | 14 - src/docs/filter_map_next.txt | 16 - src/docs/filter_next.txt | 16 - src/docs/flat_map_identity.txt | 14 - src/docs/flat_map_option.txt | 16 - src/docs/float_arithmetic.txt | 11 - src/docs/float_cmp.txt | 28 - src/docs/float_cmp_const.txt | 26 - src/docs/float_equality_without_abs.txt | 26 - src/docs/fn_address_comparisons.txt | 17 - src/docs/fn_params_excessive_bools.txt | 31 - src/docs/fn_to_numeric_cast.txt | 21 - src/docs/fn_to_numeric_cast_any.txt | 35 - .../fn_to_numeric_cast_with_truncation.txt | 26 - src/docs/for_kv_map.txt | 22 - src/docs/forget_copy.txt | 21 - src/docs/forget_non_drop.txt | 13 - src/docs/forget_ref.txt | 15 - src/docs/format_in_format_args.txt | 16 - src/docs/format_push_string.txt | 26 - src/docs/from_iter_instead_of_collect.txt | 24 - src/docs/from_over_into.txt | 26 - src/docs/from_str_radix_10.txt | 25 - src/docs/future_not_send.txt | 29 - src/docs/get_first.txt | 19 - src/docs/get_last_with_len.txt | 26 - src/docs/get_unwrap.txt | 30 - src/docs/identity_op.txt | 11 - src/docs/if_let_mutex.txt | 25 - src/docs/if_not_else.txt | 25 - src/docs/if_same_then_else.txt | 15 - src/docs/if_then_some_else_none.txt | 26 - src/docs/ifs_same_cond.txt | 25 - src/docs/implicit_clone.txt | 19 - src/docs/implicit_hasher.txt | 26 - src/docs/implicit_return.txt | 22 - src/docs/implicit_saturating_add.txt | 20 - src/docs/implicit_saturating_sub.txt | 21 - src/docs/imprecise_flops.txt | 23 - src/docs/inconsistent_digit_grouping.txt | 17 - src/docs/inconsistent_struct_constructor.txt | 40 -- src/docs/index_refutable_slice.txt | 29 - src/docs/indexing_slicing.txt | 33 - src/docs/ineffective_bit_mask.txt | 25 - src/docs/inefficient_to_string.txt | 17 - src/docs/infallible_destructuring_match.txt | 29 - src/docs/infinite_iter.txt | 13 - src/docs/inherent_to_string.txt | 29 - .../inherent_to_string_shadow_display.txt | 37 -- src/docs/init_numbered_fields.txt | 24 - src/docs/inline_always.txt | 23 - src/docs/inline_asm_x86_att_syntax.txt | 16 - src/docs/inline_asm_x86_intel_syntax.txt | 16 - src/docs/inline_fn_without_body.txt | 14 - src/docs/inspect_for_each.txt | 23 - src/docs/int_plus_one.txt | 15 - src/docs/integer_arithmetic.txt | 18 - src/docs/integer_division.txt | 19 - src/docs/into_iter_on_ref.txt | 18 - src/docs/invalid_null_ptr_usage.txt | 16 - src/docs/invalid_regex.txt | 12 - src/docs/invalid_upcast_comparisons.txt | 18 - src/docs/invalid_utf8_in_unchecked.txt | 12 - src/docs/invisible_characters.txt | 10 - src/docs/is_digit_ascii_radix.txt | 20 - src/docs/items_after_statements.txt | 37 -- src/docs/iter_cloned_collect.txt | 17 - src/docs/iter_count.txt | 22 - src/docs/iter_kv_map.txt | 22 - src/docs/iter_next_loop.txt | 17 - src/docs/iter_next_slice.txt | 16 - src/docs/iter_not_returning_iterator.txt | 26 - src/docs/iter_nth.txt | 20 - src/docs/iter_nth_zero.txt | 17 - src/docs/iter_on_empty_collections.txt | 25 - src/docs/iter_on_single_items.txt | 24 - src/docs/iter_overeager_cloned.txt | 22 - src/docs/iter_skip_next.txt | 18 - src/docs/iter_with_drain.txt | 16 - src/docs/iterator_step_by_zero.txt | 13 - src/docs/just_underscores_and_digits.txt | 14 - src/docs/large_const_arrays.txt | 17 - src/docs/large_digit_groups.txt | 12 - src/docs/large_enum_variant.txt | 41 -- src/docs/large_include_file.txt | 21 - src/docs/large_stack_arrays.txt | 10 - src/docs/large_types_passed_by_value.txt | 24 - src/docs/len_without_is_empty.txt | 19 - src/docs/len_zero.txt | 28 - src/docs/let_and_return.txt | 21 - src/docs/let_underscore_drop.txt | 29 - src/docs/let_underscore_lock.txt | 20 - src/docs/let_underscore_must_use.txt | 17 - src/docs/let_unit_value.txt | 13 - src/docs/linkedlist.txt | 32 - src/docs/lossy_float_literal.txt | 18 - src/docs/macro_use_imports.txt | 12 - src/docs/main_recursion.txt | 13 - src/docs/manual_assert.txt | 18 - src/docs/manual_async_fn.txt | 16 - src/docs/manual_bits.txt | 15 - src/docs/manual_clamp.txt | 46 -- src/docs/manual_filter.txt | 21 - src/docs/manual_filter_map.txt | 19 - src/docs/manual_find.txt | 24 - src/docs/manual_find_map.txt | 19 - src/docs/manual_flatten.txt | 25 - src/docs/manual_instant_elapsed.txt | 21 - src/docs/manual_map.txt | 17 - src/docs/manual_memcpy.txt | 18 - src/docs/manual_non_exhaustive.txt | 41 -- src/docs/manual_ok_or.txt | 19 - src/docs/manual_range_contains.txt | 19 - src/docs/manual_rem_euclid.txt | 17 - src/docs/manual_retain.txt | 15 - src/docs/manual_saturating_arithmetic.txt | 18 - src/docs/manual_split_once.txt | 29 - src/docs/manual_str_repeat.txt | 15 - src/docs/manual_string_new.txt | 20 - src/docs/manual_strip.txt | 24 - src/docs/manual_swap.txt | 22 - src/docs/manual_unwrap_or.txt | 20 - src/docs/many_single_char_names.txt | 12 - src/docs/map_clone.txt | 22 - src/docs/map_collect_result_unit.txt | 14 - src/docs/map_entry.txt | 28 - src/docs/map_err_ignore.txt | 93 --- src/docs/map_flatten.txt | 21 - src/docs/map_identity.txt | 16 - src/docs/map_unwrap_or.txt | 22 - src/docs/match_as_ref.txt | 23 - src/docs/match_bool.txt | 24 - src/docs/match_like_matches_macro.txt | 32 - src/docs/match_on_vec_items.txt | 29 - src/docs/match_overlapping_arm.txt | 16 - src/docs/match_ref_pats.txt | 26 - src/docs/match_result_ok.txt | 27 - src/docs/match_same_arms.txt | 38 -- src/docs/match_single_binding.txt | 23 - src/docs/match_str_case_mismatch.txt | 22 - src/docs/match_wild_err_arm.txt | 16 - .../match_wildcard_for_single_variants.txt | 27 - src/docs/maybe_infinite_iter.txt | 16 - src/docs/mem_forget.txt | 12 - src/docs/mem_replace_option_with_none.txt | 21 - src/docs/mem_replace_with_default.txt | 18 - src/docs/mem_replace_with_uninit.txt | 24 - src/docs/min_max.txt | 18 - src/docs/mismatched_target_os.txt | 24 - src/docs/mismatching_type_param_order.txt | 33 - src/docs/misrefactored_assign_op.txt | 20 - src/docs/missing_const_for_fn.txt | 40 -- src/docs/missing_docs_in_private_items.txt | 9 - src/docs/missing_enforced_import_renames.txt | 22 - src/docs/missing_errors_doc.txt | 21 - src/docs/missing_inline_in_public_items.txt | 45 -- src/docs/missing_panics_doc.txt | 24 - src/docs/missing_safety_doc.txt | 26 - src/docs/missing_spin_loop.txt | 27 - src/docs/missing_trait_methods.txt | 40 -- src/docs/mistyped_literal_suffixes.txt | 15 - src/docs/mixed_case_hex_literals.txt | 16 - src/docs/mixed_read_write_in_expression.txt | 32 - src/docs/mod_module_files.txt | 22 - src/docs/module_inception.txt | 24 - src/docs/module_name_repetitions.txt | 20 - src/docs/modulo_arithmetic.txt | 15 - src/docs/modulo_one.txt | 16 - src/docs/multi_assignments.txt | 17 - src/docs/multiple_crate_versions.txt | 19 - src/docs/multiple_inherent_impl.txt | 26 - src/docs/must_use_candidate.txt | 23 - src/docs/must_use_unit.txt | 13 - src/docs/mut_from_ref.txt | 26 - src/docs/mut_mut.txt | 12 - src/docs/mut_mutex_lock.txt | 29 - src/docs/mut_range_bound.txt | 29 - src/docs/mutable_key_type.txt | 61 -- src/docs/mutex_atomic.txt | 22 - src/docs/mutex_integer.txt | 22 - src/docs/naive_bytecount.txt | 22 - src/docs/needless_arbitrary_self_type.txt | 44 -- src/docs/needless_bitwise_bool.txt | 22 - src/docs/needless_bool.txt | 26 - src/docs/needless_borrow.txt | 21 - src/docs/needless_borrowed_reference.txt | 22 - src/docs/needless_collect.txt | 14 - src/docs/needless_continue.txt | 61 -- src/docs/needless_doctest_main.txt | 22 - src/docs/needless_for_each.txt | 24 - src/docs/needless_late_init.txt | 42 -- src/docs/needless_lifetimes.txt | 29 - src/docs/needless_match.txt | 36 - src/docs/needless_option_as_deref.txt | 18 - src/docs/needless_option_take.txt | 17 - .../needless_parens_on_range_literals.txt | 23 - src/docs/needless_pass_by_value.txt | 27 - src/docs/needless_question_mark.txt | 43 -- src/docs/needless_range_loop.txt | 23 - src/docs/needless_return.txt | 19 - src/docs/needless_splitn.txt | 16 - src/docs/needless_update.txt | 30 - src/docs/neg_cmp_op_on_partial_ord.txt | 26 - src/docs/neg_multiply.txt | 18 - src/docs/negative_feature_names.txt | 22 - src/docs/never_loop.txt | 15 - src/docs/new_ret_no_self.txt | 47 -- src/docs/new_without_default.txt | 32 - src/docs/no_effect.txt | 12 - src/docs/no_effect_replace.txt | 11 - src/docs/no_effect_underscore_binding.txt | 16 - src/docs/non_ascii_literal.txt | 19 - src/docs/non_octal_unix_permissions.txt | 23 - src/docs/non_send_fields_in_send_ty.txt | 36 - src/docs/nonminimal_bool.txt | 23 - src/docs/nonsensical_open_options.txt | 14 - src/docs/nonstandard_macro_braces.txt | 15 - src/docs/not_unsafe_ptr_arg_deref.txt | 30 - src/docs/obfuscated_if_else.txt | 21 - src/docs/octal_escapes.txt | 33 - src/docs/ok_expect.txt | 19 - src/docs/only_used_in_recursion.txt | 58 -- src/docs/op_ref.txt | 17 - src/docs/option_as_ref_deref.txt | 15 - src/docs/option_env_unwrap.txt | 19 - src/docs/option_filter_map.txt | 15 - src/docs/option_if_let_else.txt | 46 -- src/docs/option_map_or_none.txt | 19 - src/docs/option_map_unit_fn.txt | 27 - src/docs/option_option.txt | 32 - src/docs/or_fun_call.txt | 27 - src/docs/or_then_unwrap.txt | 22 - src/docs/out_of_bounds_indexing.txt | 22 - src/docs/overflow_check_conditional.txt | 11 - src/docs/overly_complex_bool_expr.txt | 20 - src/docs/panic.txt | 10 - src/docs/panic_in_result_fn.txt | 22 - src/docs/panicking_unwrap.txt | 18 - src/docs/partial_pub_fields.txt | 27 - src/docs/partialeq_ne_impl.txt | 18 - src/docs/partialeq_to_none.txt | 24 - src/docs/path_buf_push_overwrite.txt | 25 - src/docs/pattern_type_mismatch.txt | 64 -- src/docs/possible_missing_comma.txt | 14 - src/docs/precedence.txt | 17 - src/docs/print_in_format_impl.txt | 34 - src/docs/print_literal.txt | 16 - src/docs/print_stderr.txt | 15 - src/docs/print_stdout.txt | 15 - src/docs/print_with_newline.txt | 16 - src/docs/println_empty_string.txt | 16 - src/docs/ptr_arg.txt | 29 - src/docs/ptr_as_ptr.txt | 22 - src/docs/ptr_eq.txt | 22 - src/docs/ptr_offset_with_cast.txt | 30 - src/docs/pub_use.txt | 28 - src/docs/question_mark.txt | 18 - src/docs/range_minus_one.txt | 27 - src/docs/range_plus_one.txt | 36 - src/docs/range_zip_with_len.txt | 16 - src/docs/rc_buffer.txt | 27 - src/docs/rc_clone_in_vec_init.txt | 29 - src/docs/rc_mutex.txt | 26 - src/docs/read_zero_byte_vec.txt | 30 - src/docs/recursive_format_impl.txt | 32 - src/docs/redundant_allocation.txt | 17 - src/docs/redundant_clone.txt | 23 - src/docs/redundant_closure.txt | 25 - src/docs/redundant_closure_call.txt | 17 - .../redundant_closure_for_method_calls.txt | 15 - src/docs/redundant_else.txt | 30 - src/docs/redundant_feature_names.txt | 23 - src/docs/redundant_field_names.txt | 22 - src/docs/redundant_pattern.txt | 22 - src/docs/redundant_pattern_matching.txt | 45 -- src/docs/redundant_pub_crate.txt | 21 - src/docs/redundant_slicing.txt | 24 - src/docs/redundant_static_lifetimes.txt | 19 - src/docs/ref_binding_to_reference.txt | 21 - src/docs/ref_option_ref.txt | 19 - src/docs/repeat_once.txt | 25 - src/docs/rest_pat_in_fully_bound_structs.txt | 24 - src/docs/result_large_err.txt | 36 - src/docs/result_map_or_into_option.txt | 16 - src/docs/result_map_unit_fn.txt | 26 - src/docs/result_unit_err.txt | 40 -- src/docs/return_self_not_must_use.txt | 46 -- src/docs/reversed_empty_ranges.txt | 26 - src/docs/same_functions_in_if_condition.txt | 41 -- src/docs/same_item_push.txt | 29 - src/docs/same_name_method.txt | 23 - src/docs/search_is_some.txt | 24 - src/docs/self_assignment.txt | 32 - src/docs/self_named_constructors.txt | 26 - src/docs/self_named_module_files.txt | 22 - src/docs/semicolon_if_nothing_returned.txt | 20 - src/docs/separated_literal_suffix.txt | 17 - src/docs/serde_api_misuse.txt | 10 - src/docs/shadow_reuse.txt | 20 - src/docs/shadow_same.txt | 18 - src/docs/shadow_unrelated.txt | 22 - src/docs/short_circuit_statement.txt | 14 - src/docs/should_implement_trait.txt | 22 - src/docs/significant_drop_in_scrutinee.txt | 43 -- src/docs/similar_names.txt | 16 - src/docs/single_char_add_str.txt | 18 - src/docs/single_char_lifetime_names.txt | 28 - src/docs/single_char_pattern.txt | 20 - src/docs/single_component_path_imports.txt | 21 - src/docs/single_element_loop.txt | 21 - src/docs/single_match.txt | 21 - src/docs/single_match_else.txt | 29 - src/docs/size_of_in_element_count.txt | 16 - src/docs/skip_while_next.txt | 16 - src/docs/slow_vector_initialization.txt | 24 - src/docs/stable_sort_primitive.txt | 31 - src/docs/std_instead_of_alloc.txt | 18 - src/docs/std_instead_of_core.txt | 18 - src/docs/str_to_string.txt | 18 - src/docs/string_add.txt | 27 - src/docs/string_add_assign.txt | 17 - src/docs/string_extend_chars.txt | 23 - src/docs/string_from_utf8_as_bytes.txt | 15 - src/docs/string_lit_as_bytes.txt | 39 -- src/docs/string_slice.txt | 17 - src/docs/string_to_string.txt | 19 - src/docs/strlen_on_c_strings.txt | 20 - src/docs/struct_excessive_bools.txt | 29 - src/docs/suboptimal_flops.txt | 50 -- src/docs/suspicious_arithmetic_impl.txt | 17 - src/docs/suspicious_assignment_formatting.txt | 12 - src/docs/suspicious_else_formatting.txt | 30 - src/docs/suspicious_map.txt | 13 - src/docs/suspicious_op_assign_impl.txt | 15 - src/docs/suspicious_operation_groupings.txt | 41 -- src/docs/suspicious_splitn.txt | 22 - src/docs/suspicious_to_owned.txt | 39 -- src/docs/suspicious_unary_op_formatting.txt | 18 - src/docs/swap_ptr_to_ref.txt | 23 - src/docs/tabs_in_doc_comments.txt | 44 -- src/docs/temporary_assignment.txt | 12 - src/docs/to_digit_is_some.txt | 15 - src/docs/to_string_in_format_args.txt | 17 - src/docs/todo.txt | 10 - src/docs/too_many_arguments.txt | 14 - src/docs/too_many_lines.txt | 17 - src/docs/toplevel_ref_arg.txt | 28 - src/docs/trailing_empty_array.txt | 22 - src/docs/trait_duplication_in_bounds.txt | 37 -- src/docs/transmute_bytes_to_str.txt | 27 - src/docs/transmute_float_to_int.txt | 16 - src/docs/transmute_int_to_bool.txt | 16 - src/docs/transmute_int_to_char.txt | 27 - src/docs/transmute_int_to_float.txt | 16 - src/docs/transmute_num_to_bytes.txt | 16 - src/docs/transmute_ptr_to_ptr.txt | 21 - src/docs/transmute_ptr_to_ref.txt | 21 - src/docs/transmute_undefined_repr.txt | 22 - .../transmutes_expressible_as_ptr_casts.txt | 16 - src/docs/transmuting_null.txt | 15 - src/docs/trim_split_whitespace.txt | 14 - src/docs/trivial_regex.txt | 18 - src/docs/trivially_copy_pass_by_ref.txt | 43 -- src/docs/try_err.txt | 28 - src/docs/type_complexity.txt | 14 - src/docs/type_repetition_in_bounds.txt | 16 - src/docs/undocumented_unsafe_blocks.txt | 43 -- src/docs/undropped_manually_drops.txt | 22 - src/docs/unicode_not_nfc.txt | 12 - src/docs/unimplemented.txt | 10 - src/docs/uninit_assumed_init.txt | 28 - src/docs/uninit_vec.txt | 41 -- src/docs/uninlined_format_args.txt | 36 - src/docs/unit_arg.txt | 14 - src/docs/unit_cmp.txt | 33 - src/docs/unit_hash.txt | 20 - src/docs/unit_return_expecting_ord.txt | 20 - src/docs/unnecessary_cast.txt | 19 - src/docs/unnecessary_filter_map.txt | 23 - src/docs/unnecessary_find_map.txt | 23 - src/docs/unnecessary_fold.txt | 17 - src/docs/unnecessary_join.txt | 25 - src/docs/unnecessary_lazy_evaluations.txt | 32 - src/docs/unnecessary_mut_passed.txt | 17 - src/docs/unnecessary_operation.txt | 12 - src/docs/unnecessary_owned_empty_strings.txt | 16 - src/docs/unnecessary_self_imports.txt | 19 - src/docs/unnecessary_sort_by.txt | 21 - src/docs/unnecessary_to_owned.txt | 24 - src/docs/unnecessary_unwrap.txt | 20 - src/docs/unnecessary_wraps.txt | 36 - src/docs/unneeded_field_pattern.txt | 26 - src/docs/unneeded_wildcard_pattern.txt | 28 - src/docs/unnested_or_patterns.txt | 22 - src/docs/unreachable.txt | 10 - src/docs/unreadable_literal.txt | 16 - src/docs/unsafe_derive_deserialize.txt | 27 - src/docs/unsafe_removed_from_name.txt | 15 - src/docs/unseparated_literal_suffix.txt | 18 - src/docs/unsound_collection_transmute.txt | 25 - src/docs/unused_async.txt | 23 - src/docs/unused_format_specs.txt | 24 - src/docs/unused_io_amount.txt | 31 - src/docs/unused_peekable.txt | 26 - src/docs/unused_rounding.txt | 17 - src/docs/unused_self.txt | 23 - src/docs/unused_unit.txt | 18 - src/docs/unusual_byte_groupings.txt | 12 - src/docs/unwrap_in_result.txt | 39 -- src/docs/unwrap_or_else_default.txt | 18 - src/docs/unwrap_used.txt | 37 -- src/docs/upper_case_acronyms.txt | 25 - src/docs/use_debug.txt | 12 - src/docs/use_self.txt | 31 - src/docs/used_underscore_binding.txt | 19 - src/docs/useless_asref.txt | 17 - src/docs/useless_attribute.txt | 36 - src/docs/useless_conversion.txt | 17 - src/docs/useless_format.txt | 22 - src/docs/useless_let_if_seq.txt | 39 -- src/docs/useless_transmute.txt | 12 - src/docs/useless_vec.txt | 18 - src/docs/vec_box.txt | 26 - src/docs/vec_init_then_push.txt | 23 - src/docs/vec_resize_to_zero.txt | 15 - src/docs/verbose_bit_mask.txt | 15 - src/docs/verbose_file_reads.txt | 17 - src/docs/vtable_address_comparisons.txt | 17 - src/docs/while_immutable_condition.txt | 20 - src/docs/while_let_loop.txt | 25 - src/docs/while_let_on_iterator.txt | 20 - src/docs/wildcard_dependencies.txt | 13 - src/docs/wildcard_enum_match_arm.txt | 25 - src/docs/wildcard_imports.txt | 45 -- src/docs/wildcard_in_or_patterns.txt | 22 - src/docs/write_literal.txt | 17 - src/docs/write_with_newline.txt | 18 - src/docs/writeln_empty_string.txt | 16 - src/docs/wrong_self_convention.txt | 39 -- src/docs/wrong_transmute.txt | 15 - src/docs/zero_divided_by_zero.txt | 15 - src/docs/zero_prefixed_literal.txt | 32 - src/docs/zero_ptr.txt | 16 - src/docs/zero_sized_map_values.txt | 24 - src/docs/zst_offset.txt | 11 - src/driver.rs | 55 +- src/main.rs | 4 +- .../fail_both_diff/src/main.stderr | 8 +- .../fail_both_same/src/main.stderr | 8 +- .../fail_cargo/src/main.stderr | 8 +- .../fail_clippy/src/main.stderr | 8 +- .../fail_file_attr/src/main.stderr | 8 +- tests/ui-internal/custom_ice_message.rs | 1 + tests/ui-internal/custom_ice_message.stderr | 2 +- ...unnecessary_def_path_hardcoded_path.stderr | 18 +- .../arithmetic_side_effects_allowed.rs | 11 +- .../await_holding_invalid_type.stderr | 4 +- tests/ui-toml/expect_used/expect_used.rs | 23 +- tests/ui-toml/expect_used/expect_used.stderr | 4 +- tests/ui-toml/mut_key/clippy.toml | 1 + tests/ui-toml/mut_key/mut_key.rs | 53 ++ tests/ui-toml/print_macro/clippy.toml | 1 + tests/ui-toml/print_macro/print_macro.rs | 20 + tests/ui-toml/print_macro/print_macro.stderr | 18 + .../toml_disallowed_methods/clippy.toml | 6 + .../conf_disallowed_methods.rs | 30 + .../conf_disallowed_methods.stderr | 50 +- .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui-toml/unwrap_used/unwrap_used.rs | 21 +- tests/ui-toml/unwrap_used/unwrap_used.stderr | 32 +- tests/ui-toml/vec_box_sized/test.rs | 5 +- tests/ui-toml/vec_box_sized/test.stderr | 6 +- tests/ui/arithmetic_side_effects.rs | 8 +- tests/ui/arithmetic_side_effects.stderr | 110 +-- tests/ui/auxiliary/doc_unsafe_macros.rs | 8 + tests/ui/blanket_clippy_restriction_lints.rs | 2 + .../blanket_clippy_restriction_lints.stderr | 27 +- tests/ui/bool_to_int_with_if.fixed | 22 + tests/ui/bool_to_int_with_if.rs | 22 + tests/ui/bool_to_int_with_if.stderr | 18 +- tests/ui/cognitive_complexity.rs | 16 + tests/ui/cognitive_complexity.stderr | 18 +- tests/ui/crashes/ice-2774.stderr | 2 +- tests/ui/crashes/ice-9746.rs | 15 + .../needless_lifetimes_impl_trait.stderr | 2 +- .../no_std_main_recursion.rs | 1 - tests/ui/doc_errors.stderr | 36 +- tests/ui/doc_unnecessary_unsafe.rs | 148 +++++ tests/ui/doc_unnecessary_unsafe.stderr | 51 ++ tests/ui/doc_unsafe.stderr | 34 +- tests/ui/eq_op.rs | 1 + tests/ui/eq_op.stderr | 56 +- tests/ui/equatable_if_let.fixed | 11 +- tests/ui/equatable_if_let.rs | 7 + tests/ui/equatable_if_let.stderr | 42 +- tests/ui/expect.stderr | 6 +- tests/ui/explicit_auto_deref.fixed | 11 + tests/ui/explicit_auto_deref.rs | 11 + tests/ui/fn_params_excessive_bools.rs | 8 + tests/ui/fn_params_excessive_bools.stderr | 22 +- tests/ui/from_raw_with_void_ptr.rs | 34 + tests/ui/from_raw_with_void_ptr.stderr | 63 ++ tests/ui/get_unwrap.stderr | 26 +- tests/ui/infallible_destructuring_match.fixed | 12 + tests/ui/infallible_destructuring_match.rs | 14 + .../ui/infallible_destructuring_match.stderr | 16 +- tests/ui/issue_4266.stderr | 4 +- tests/ui/let_underscore_drop.rs | 28 - tests/ui/let_underscore_drop.stderr | 27 - tests/ui/let_underscore_future.rs | 20 + tests/ui/let_underscore_future.stderr | 27 + tests/ui/let_underscore_lock.rs | 31 +- tests/ui/let_underscore_lock.stderr | 66 +- tests/ui/let_underscore_must_use.stderr | 24 +- tests/ui/manual_flatten.rs | 2 +- tests/ui/manual_instant_elapsed.fixed | 1 + tests/ui/manual_instant_elapsed.rs | 1 + tests/ui/manual_instant_elapsed.stderr | 4 +- tests/ui/manual_is_ascii_check.fixed | 45 ++ tests/ui/manual_is_ascii_check.rs | 45 ++ tests/ui/manual_is_ascii_check.stderr | 70 ++ tests/ui/manual_let_else.rs | 237 +++++++ tests/ui/manual_let_else.stderr | 263 ++++++++ tests/ui/manual_let_else_match.rs | 121 ++++ tests/ui/manual_let_else_match.stderr | 58 ++ tests/ui/manual_ok_or.fixed | 1 + tests/ui/manual_ok_or.rs | 1 + tests/ui/manual_ok_or.stderr | 8 +- tests/ui/map_flatten_fixable.fixed | 1 - tests/ui/map_flatten_fixable.rs | 1 - tests/ui/map_flatten_fixable.stderr | 18 +- tests/ui/match_expr_like_matches_macro.fixed | 7 +- tests/ui/match_expr_like_matches_macro.rs | 7 +- tests/ui/match_expr_like_matches_macro.stderr | 28 +- tests/ui/missing_panics_doc.stderr | 49 +- tests/ui/mut_from_ref.rs | 2 +- tests/ui/mut_mut.rs | 17 + tests/ui/mut_range_bound.rs | 2 +- tests/ui/mut_range_bound.stderr | 2 +- tests/ui/needless_borrow.fixed | 125 ++++ tests/ui/needless_borrow.rs | 125 ++++ tests/ui/needless_borrow.stderr | 8 +- tests/ui/needless_borrowed_ref.fixed | 67 +- tests/ui/needless_borrowed_ref.rs | 67 +- tests/ui/needless_borrowed_ref.stderr | 113 +++- tests/ui/needless_collect.fixed | 29 + tests/ui/needless_collect.rs | 29 + tests/ui/needless_collect.stderr | 26 +- tests/ui/needless_collect_indirect.rs | 1 + tests/ui/needless_collect_indirect.stderr | 32 +- tests/ui/needless_lifetimes.rs | 96 ++- tests/ui/needless_lifetimes.stderr | 246 +++++-- tests/ui/never_loop.rs | 21 + tests/ui/never_loop.stderr | 14 +- tests/ui/new_ret_no_self.rs | 50 ++ tests/ui/new_ret_no_self.stderr | 18 +- tests/ui/option_if_let_else.fixed | 9 + tests/ui/option_if_let_else.rs | 9 + tests/ui/or_fun_call.fixed | 16 + tests/ui/or_fun_call.rs | 16 + tests/ui/or_fun_call.stderr | 14 +- tests/ui/question_mark.fixed | 3 + tests/ui/question_mark.rs | 3 + tests/ui/question_mark.stderr | 4 +- tests/ui/rename.fixed | 8 +- tests/ui/rename.rs | 8 +- tests/ui/rename.stderr | 106 +-- tests/ui/result_large_err.rs | 12 + tests/ui/result_large_err.stderr | 30 +- tests/ui/seek_from_current.fixed | 26 + tests/ui/seek_from_current.rs | 26 + tests/ui/seek_from_current.stderr | 10 + .../ui/seek_to_start_instead_of_rewind.fixed | 137 ++++ tests/ui/seek_to_start_instead_of_rewind.rs | 137 ++++ .../ui/seek_to_start_instead_of_rewind.stderr | 22 + tests/ui/single_component_path_imports.stderr | 12 +- ...component_path_imports_nested_first.stderr | 15 +- tests/ui/string_extend.fixed | 3 + tests/ui/string_extend.rs | 3 + tests/ui/string_extend.stderr | 8 +- tests/ui/suspicious_xor_used_as_pow.rs | 34 + tests/ui/suspicious_xor_used_as_pow.stderr | 51 ++ tests/ui/swap.fixed | 9 + tests/ui/swap.rs | 9 + tests/ui/transmute.rs | 2 +- tests/ui/trivially_copy_pass_by_ref.rs | 1 + tests/ui/trivially_copy_pass_by_ref.stderr | 36 +- tests/ui/unchecked_duration_subtraction.fixed | 17 + tests/ui/unchecked_duration_subtraction.rs | 17 + .../ui/unchecked_duration_subtraction.stderr | 28 + tests/ui/undocumented_unsafe_blocks.rs | 19 + tests/ui/undocumented_unsafe_blocks.stderr | 26 +- tests/ui/unnecessary_join.stderr | 4 +- tests/ui/unused_rounding.fixed | 5 + tests/ui/unused_rounding.rs | 5 + tests/ui/unused_rounding.stderr | 8 +- tests/ui/unused_unit.fixed | 7 + tests/ui/unused_unit.rs | 7 + tests/ui/unused_unit.stderr | 40 +- tests/ui/unwrap.stderr | 6 +- tests/ui/unwrap_expect_used.stderr | 12 +- tests/ui/unwrap_or.rs | 2 +- tests/ui/use_self_trait.fixed | 41 +- tests/ui/use_self_trait.rs | 39 +- tests/ui/use_self_trait.stderr | 14 +- tests/ui/useless_attribute.fixed | 21 +- tests/ui/useless_attribute.rs | 21 +- tests/ui/useless_attribute.stderr | 6 +- tests/versioncheck.rs | 15 +- 895 files changed, 8241 insertions(+), 18373 deletions(-) create mode 100644 clippy_lints/src/declared_lints.rs create mode 100644 clippy_lints/src/from_raw_with_void_ptr.rs create mode 100644 clippy_lints/src/instant_subtraction.rs delete mode 100644 clippy_lints/src/lib.register_all.rs delete mode 100644 clippy_lints/src/lib.register_cargo.rs delete mode 100644 clippy_lints/src/lib.register_complexity.rs delete mode 100644 clippy_lints/src/lib.register_correctness.rs delete mode 100644 clippy_lints/src/lib.register_internal.rs delete mode 100644 clippy_lints/src/lib.register_lints.rs delete mode 100644 clippy_lints/src/lib.register_nursery.rs delete mode 100644 clippy_lints/src/lib.register_pedantic.rs delete mode 100644 clippy_lints/src/lib.register_perf.rs delete mode 100644 clippy_lints/src/lib.register_restriction.rs delete mode 100644 clippy_lints/src/lib.register_style.rs delete mode 100644 clippy_lints/src/lib.register_suspicious.rs delete mode 100644 clippy_lints/src/manual_instant_elapsed.rs create mode 100644 clippy_lints/src/manual_is_ascii_check.rs create mode 100644 clippy_lints/src/manual_let_else.rs rename clippy_lints/src/{loops => methods}/needless_collect.rs (62%) create mode 100644 clippy_lints/src/methods/seek_from_current.rs create mode 100644 clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs create mode 100644 clippy_lints/src/suspicious_xor_used_as_pow.rs create mode 100644 declare_clippy_lint/Cargo.toml create mode 100644 declare_clippy_lint/src/lib.rs delete mode 100644 src/docs.rs delete mode 100644 src/docs/absurd_extreme_comparisons.txt delete mode 100644 src/docs/alloc_instead_of_core.txt delete mode 100644 src/docs/allow_attributes_without_reason.txt delete mode 100644 src/docs/almost_complete_letter_range.txt delete mode 100644 src/docs/almost_swapped.txt delete mode 100644 src/docs/approx_constant.txt delete mode 100644 src/docs/arithmetic_side_effects.txt delete mode 100644 src/docs/as_conversions.txt delete mode 100644 src/docs/as_ptr_cast_mut.txt delete mode 100644 src/docs/as_underscore.txt delete mode 100644 src/docs/assertions_on_constants.txt delete mode 100644 src/docs/assertions_on_result_states.txt delete mode 100644 src/docs/assign_op_pattern.txt delete mode 100644 src/docs/async_yields_async.txt delete mode 100644 src/docs/await_holding_invalid_type.txt delete mode 100644 src/docs/await_holding_lock.txt delete mode 100644 src/docs/await_holding_refcell_ref.txt delete mode 100644 src/docs/bad_bit_mask.txt delete mode 100644 src/docs/bind_instead_of_map.txt delete mode 100644 src/docs/blanket_clippy_restriction_lints.txt delete mode 100644 src/docs/blocks_in_if_conditions.txt delete mode 100644 src/docs/bool_assert_comparison.txt delete mode 100644 src/docs/bool_comparison.txt delete mode 100644 src/docs/bool_to_int_with_if.txt delete mode 100644 src/docs/borrow_as_ptr.txt delete mode 100644 src/docs/borrow_deref_ref.txt delete mode 100644 src/docs/borrow_interior_mutable_const.txt delete mode 100644 src/docs/borrowed_box.txt delete mode 100644 src/docs/box_collection.txt delete mode 100644 src/docs/box_default.txt delete mode 100644 src/docs/boxed_local.txt delete mode 100644 src/docs/branches_sharing_code.txt delete mode 100644 src/docs/builtin_type_shadow.txt delete mode 100644 src/docs/bytes_count_to_len.txt delete mode 100644 src/docs/bytes_nth.txt delete mode 100644 src/docs/cargo_common_metadata.txt delete mode 100644 src/docs/case_sensitive_file_extension_comparisons.txt delete mode 100644 src/docs/cast_abs_to_unsigned.txt delete mode 100644 src/docs/cast_enum_constructor.txt delete mode 100644 src/docs/cast_enum_truncation.txt delete mode 100644 src/docs/cast_lossless.txt delete mode 100644 src/docs/cast_nan_to_int.txt delete mode 100644 src/docs/cast_possible_truncation.txt delete mode 100644 src/docs/cast_possible_wrap.txt delete mode 100644 src/docs/cast_precision_loss.txt delete mode 100644 src/docs/cast_ptr_alignment.txt delete mode 100644 src/docs/cast_ref_to_mut.txt delete mode 100644 src/docs/cast_sign_loss.txt delete mode 100644 src/docs/cast_slice_different_sizes.txt delete mode 100644 src/docs/cast_slice_from_raw_parts.txt delete mode 100644 src/docs/char_lit_as_u8.txt delete mode 100644 src/docs/chars_last_cmp.txt delete mode 100644 src/docs/chars_next_cmp.txt delete mode 100644 src/docs/checked_conversions.txt delete mode 100644 src/docs/clone_double_ref.txt delete mode 100644 src/docs/clone_on_copy.txt delete mode 100644 src/docs/clone_on_ref_ptr.txt delete mode 100644 src/docs/cloned_instead_of_copied.txt delete mode 100644 src/docs/cmp_nan.txt delete mode 100644 src/docs/cmp_null.txt delete mode 100644 src/docs/cmp_owned.txt delete mode 100644 src/docs/cognitive_complexity.txt delete mode 100644 src/docs/collapsible_else_if.txt delete mode 100644 src/docs/collapsible_if.txt delete mode 100644 src/docs/collapsible_match.txt delete mode 100644 src/docs/collapsible_str_replace.txt delete mode 100644 src/docs/comparison_chain.txt delete mode 100644 src/docs/comparison_to_empty.txt delete mode 100644 src/docs/copy_iterator.txt delete mode 100644 src/docs/crate_in_macro_def.txt delete mode 100644 src/docs/create_dir.txt delete mode 100644 src/docs/crosspointer_transmute.txt delete mode 100644 src/docs/dbg_macro.txt delete mode 100644 src/docs/debug_assert_with_mut_call.txt delete mode 100644 src/docs/decimal_literal_representation.txt delete mode 100644 src/docs/declare_interior_mutable_const.txt delete mode 100644 src/docs/default_instead_of_iter_empty.txt delete mode 100644 src/docs/default_numeric_fallback.txt delete mode 100644 src/docs/default_trait_access.txt delete mode 100644 src/docs/default_union_representation.txt delete mode 100644 src/docs/deprecated_cfg_attr.txt delete mode 100644 src/docs/deprecated_semver.txt delete mode 100644 src/docs/deref_addrof.txt delete mode 100644 src/docs/deref_by_slicing.txt delete mode 100644 src/docs/derivable_impls.txt delete mode 100644 src/docs/derive_hash_xor_eq.txt delete mode 100644 src/docs/derive_ord_xor_partial_ord.txt delete mode 100644 src/docs/derive_partial_eq_without_eq.txt delete mode 100644 src/docs/disallowed_macros.txt delete mode 100644 src/docs/disallowed_methods.txt delete mode 100644 src/docs/disallowed_names.txt delete mode 100644 src/docs/disallowed_script_idents.txt delete mode 100644 src/docs/disallowed_types.txt delete mode 100644 src/docs/diverging_sub_expression.txt delete mode 100644 src/docs/doc_link_with_quotes.txt delete mode 100644 src/docs/doc_markdown.txt delete mode 100644 src/docs/double_comparisons.txt delete mode 100644 src/docs/double_must_use.txt delete mode 100644 src/docs/double_neg.txt delete mode 100644 src/docs/double_parens.txt delete mode 100644 src/docs/drop_copy.txt delete mode 100644 src/docs/drop_non_drop.txt delete mode 100644 src/docs/drop_ref.txt delete mode 100644 src/docs/duplicate_mod.txt delete mode 100644 src/docs/duplicate_underscore_argument.txt delete mode 100644 src/docs/duration_subsec.txt delete mode 100644 src/docs/else_if_without_else.txt delete mode 100644 src/docs/empty_drop.txt delete mode 100644 src/docs/empty_enum.txt delete mode 100644 src/docs/empty_line_after_outer_attr.txt delete mode 100644 src/docs/empty_loop.txt delete mode 100644 src/docs/empty_structs_with_brackets.txt delete mode 100644 src/docs/enum_clike_unportable_variant.txt delete mode 100644 src/docs/enum_glob_use.txt delete mode 100644 src/docs/enum_variant_names.txt delete mode 100644 src/docs/eq_op.txt delete mode 100644 src/docs/equatable_if_let.txt delete mode 100644 src/docs/erasing_op.txt delete mode 100644 src/docs/err_expect.txt delete mode 100644 src/docs/excessive_precision.txt delete mode 100644 src/docs/exhaustive_enums.txt delete mode 100644 src/docs/exhaustive_structs.txt delete mode 100644 src/docs/exit.txt delete mode 100644 src/docs/expect_fun_call.txt delete mode 100644 src/docs/expect_used.txt delete mode 100644 src/docs/expl_impl_clone_on_copy.txt delete mode 100644 src/docs/explicit_auto_deref.txt delete mode 100644 src/docs/explicit_counter_loop.txt delete mode 100644 src/docs/explicit_deref_methods.txt delete mode 100644 src/docs/explicit_into_iter_loop.txt delete mode 100644 src/docs/explicit_iter_loop.txt delete mode 100644 src/docs/explicit_write.txt delete mode 100644 src/docs/extend_with_drain.txt delete mode 100644 src/docs/extra_unused_lifetimes.txt delete mode 100644 src/docs/fallible_impl_from.txt delete mode 100644 src/docs/field_reassign_with_default.txt delete mode 100644 src/docs/filetype_is_file.txt delete mode 100644 src/docs/filter_map_identity.txt delete mode 100644 src/docs/filter_map_next.txt delete mode 100644 src/docs/filter_next.txt delete mode 100644 src/docs/flat_map_identity.txt delete mode 100644 src/docs/flat_map_option.txt delete mode 100644 src/docs/float_arithmetic.txt delete mode 100644 src/docs/float_cmp.txt delete mode 100644 src/docs/float_cmp_const.txt delete mode 100644 src/docs/float_equality_without_abs.txt delete mode 100644 src/docs/fn_address_comparisons.txt delete mode 100644 src/docs/fn_params_excessive_bools.txt delete mode 100644 src/docs/fn_to_numeric_cast.txt delete mode 100644 src/docs/fn_to_numeric_cast_any.txt delete mode 100644 src/docs/fn_to_numeric_cast_with_truncation.txt delete mode 100644 src/docs/for_kv_map.txt delete mode 100644 src/docs/forget_copy.txt delete mode 100644 src/docs/forget_non_drop.txt delete mode 100644 src/docs/forget_ref.txt delete mode 100644 src/docs/format_in_format_args.txt delete mode 100644 src/docs/format_push_string.txt delete mode 100644 src/docs/from_iter_instead_of_collect.txt delete mode 100644 src/docs/from_over_into.txt delete mode 100644 src/docs/from_str_radix_10.txt delete mode 100644 src/docs/future_not_send.txt delete mode 100644 src/docs/get_first.txt delete mode 100644 src/docs/get_last_with_len.txt delete mode 100644 src/docs/get_unwrap.txt delete mode 100644 src/docs/identity_op.txt delete mode 100644 src/docs/if_let_mutex.txt delete mode 100644 src/docs/if_not_else.txt delete mode 100644 src/docs/if_same_then_else.txt delete mode 100644 src/docs/if_then_some_else_none.txt delete mode 100644 src/docs/ifs_same_cond.txt delete mode 100644 src/docs/implicit_clone.txt delete mode 100644 src/docs/implicit_hasher.txt delete mode 100644 src/docs/implicit_return.txt delete mode 100644 src/docs/implicit_saturating_add.txt delete mode 100644 src/docs/implicit_saturating_sub.txt delete mode 100644 src/docs/imprecise_flops.txt delete mode 100644 src/docs/inconsistent_digit_grouping.txt delete mode 100644 src/docs/inconsistent_struct_constructor.txt delete mode 100644 src/docs/index_refutable_slice.txt delete mode 100644 src/docs/indexing_slicing.txt delete mode 100644 src/docs/ineffective_bit_mask.txt delete mode 100644 src/docs/inefficient_to_string.txt delete mode 100644 src/docs/infallible_destructuring_match.txt delete mode 100644 src/docs/infinite_iter.txt delete mode 100644 src/docs/inherent_to_string.txt delete mode 100644 src/docs/inherent_to_string_shadow_display.txt delete mode 100644 src/docs/init_numbered_fields.txt delete mode 100644 src/docs/inline_always.txt delete mode 100644 src/docs/inline_asm_x86_att_syntax.txt delete mode 100644 src/docs/inline_asm_x86_intel_syntax.txt delete mode 100644 src/docs/inline_fn_without_body.txt delete mode 100644 src/docs/inspect_for_each.txt delete mode 100644 src/docs/int_plus_one.txt delete mode 100644 src/docs/integer_arithmetic.txt delete mode 100644 src/docs/integer_division.txt delete mode 100644 src/docs/into_iter_on_ref.txt delete mode 100644 src/docs/invalid_null_ptr_usage.txt delete mode 100644 src/docs/invalid_regex.txt delete mode 100644 src/docs/invalid_upcast_comparisons.txt delete mode 100644 src/docs/invalid_utf8_in_unchecked.txt delete mode 100644 src/docs/invisible_characters.txt delete mode 100644 src/docs/is_digit_ascii_radix.txt delete mode 100644 src/docs/items_after_statements.txt delete mode 100644 src/docs/iter_cloned_collect.txt delete mode 100644 src/docs/iter_count.txt delete mode 100644 src/docs/iter_kv_map.txt delete mode 100644 src/docs/iter_next_loop.txt delete mode 100644 src/docs/iter_next_slice.txt delete mode 100644 src/docs/iter_not_returning_iterator.txt delete mode 100644 src/docs/iter_nth.txt delete mode 100644 src/docs/iter_nth_zero.txt delete mode 100644 src/docs/iter_on_empty_collections.txt delete mode 100644 src/docs/iter_on_single_items.txt delete mode 100644 src/docs/iter_overeager_cloned.txt delete mode 100644 src/docs/iter_skip_next.txt delete mode 100644 src/docs/iter_with_drain.txt delete mode 100644 src/docs/iterator_step_by_zero.txt delete mode 100644 src/docs/just_underscores_and_digits.txt delete mode 100644 src/docs/large_const_arrays.txt delete mode 100644 src/docs/large_digit_groups.txt delete mode 100644 src/docs/large_enum_variant.txt delete mode 100644 src/docs/large_include_file.txt delete mode 100644 src/docs/large_stack_arrays.txt delete mode 100644 src/docs/large_types_passed_by_value.txt delete mode 100644 src/docs/len_without_is_empty.txt delete mode 100644 src/docs/len_zero.txt delete mode 100644 src/docs/let_and_return.txt delete mode 100644 src/docs/let_underscore_drop.txt delete mode 100644 src/docs/let_underscore_lock.txt delete mode 100644 src/docs/let_underscore_must_use.txt delete mode 100644 src/docs/let_unit_value.txt delete mode 100644 src/docs/linkedlist.txt delete mode 100644 src/docs/lossy_float_literal.txt delete mode 100644 src/docs/macro_use_imports.txt delete mode 100644 src/docs/main_recursion.txt delete mode 100644 src/docs/manual_assert.txt delete mode 100644 src/docs/manual_async_fn.txt delete mode 100644 src/docs/manual_bits.txt delete mode 100644 src/docs/manual_clamp.txt delete mode 100644 src/docs/manual_filter.txt delete mode 100644 src/docs/manual_filter_map.txt delete mode 100644 src/docs/manual_find.txt delete mode 100644 src/docs/manual_find_map.txt delete mode 100644 src/docs/manual_flatten.txt delete mode 100644 src/docs/manual_instant_elapsed.txt delete mode 100644 src/docs/manual_map.txt delete mode 100644 src/docs/manual_memcpy.txt delete mode 100644 src/docs/manual_non_exhaustive.txt delete mode 100644 src/docs/manual_ok_or.txt delete mode 100644 src/docs/manual_range_contains.txt delete mode 100644 src/docs/manual_rem_euclid.txt delete mode 100644 src/docs/manual_retain.txt delete mode 100644 src/docs/manual_saturating_arithmetic.txt delete mode 100644 src/docs/manual_split_once.txt delete mode 100644 src/docs/manual_str_repeat.txt delete mode 100644 src/docs/manual_string_new.txt delete mode 100644 src/docs/manual_strip.txt delete mode 100644 src/docs/manual_swap.txt delete mode 100644 src/docs/manual_unwrap_or.txt delete mode 100644 src/docs/many_single_char_names.txt delete mode 100644 src/docs/map_clone.txt delete mode 100644 src/docs/map_collect_result_unit.txt delete mode 100644 src/docs/map_entry.txt delete mode 100644 src/docs/map_err_ignore.txt delete mode 100644 src/docs/map_flatten.txt delete mode 100644 src/docs/map_identity.txt delete mode 100644 src/docs/map_unwrap_or.txt delete mode 100644 src/docs/match_as_ref.txt delete mode 100644 src/docs/match_bool.txt delete mode 100644 src/docs/match_like_matches_macro.txt delete mode 100644 src/docs/match_on_vec_items.txt delete mode 100644 src/docs/match_overlapping_arm.txt delete mode 100644 src/docs/match_ref_pats.txt delete mode 100644 src/docs/match_result_ok.txt delete mode 100644 src/docs/match_same_arms.txt delete mode 100644 src/docs/match_single_binding.txt delete mode 100644 src/docs/match_str_case_mismatch.txt delete mode 100644 src/docs/match_wild_err_arm.txt delete mode 100644 src/docs/match_wildcard_for_single_variants.txt delete mode 100644 src/docs/maybe_infinite_iter.txt delete mode 100644 src/docs/mem_forget.txt delete mode 100644 src/docs/mem_replace_option_with_none.txt delete mode 100644 src/docs/mem_replace_with_default.txt delete mode 100644 src/docs/mem_replace_with_uninit.txt delete mode 100644 src/docs/min_max.txt delete mode 100644 src/docs/mismatched_target_os.txt delete mode 100644 src/docs/mismatching_type_param_order.txt delete mode 100644 src/docs/misrefactored_assign_op.txt delete mode 100644 src/docs/missing_const_for_fn.txt delete mode 100644 src/docs/missing_docs_in_private_items.txt delete mode 100644 src/docs/missing_enforced_import_renames.txt delete mode 100644 src/docs/missing_errors_doc.txt delete mode 100644 src/docs/missing_inline_in_public_items.txt delete mode 100644 src/docs/missing_panics_doc.txt delete mode 100644 src/docs/missing_safety_doc.txt delete mode 100644 src/docs/missing_spin_loop.txt delete mode 100644 src/docs/missing_trait_methods.txt delete mode 100644 src/docs/mistyped_literal_suffixes.txt delete mode 100644 src/docs/mixed_case_hex_literals.txt delete mode 100644 src/docs/mixed_read_write_in_expression.txt delete mode 100644 src/docs/mod_module_files.txt delete mode 100644 src/docs/module_inception.txt delete mode 100644 src/docs/module_name_repetitions.txt delete mode 100644 src/docs/modulo_arithmetic.txt delete mode 100644 src/docs/modulo_one.txt delete mode 100644 src/docs/multi_assignments.txt delete mode 100644 src/docs/multiple_crate_versions.txt delete mode 100644 src/docs/multiple_inherent_impl.txt delete mode 100644 src/docs/must_use_candidate.txt delete mode 100644 src/docs/must_use_unit.txt delete mode 100644 src/docs/mut_from_ref.txt delete mode 100644 src/docs/mut_mut.txt delete mode 100644 src/docs/mut_mutex_lock.txt delete mode 100644 src/docs/mut_range_bound.txt delete mode 100644 src/docs/mutable_key_type.txt delete mode 100644 src/docs/mutex_atomic.txt delete mode 100644 src/docs/mutex_integer.txt delete mode 100644 src/docs/naive_bytecount.txt delete mode 100644 src/docs/needless_arbitrary_self_type.txt delete mode 100644 src/docs/needless_bitwise_bool.txt delete mode 100644 src/docs/needless_bool.txt delete mode 100644 src/docs/needless_borrow.txt delete mode 100644 src/docs/needless_borrowed_reference.txt delete mode 100644 src/docs/needless_collect.txt delete mode 100644 src/docs/needless_continue.txt delete mode 100644 src/docs/needless_doctest_main.txt delete mode 100644 src/docs/needless_for_each.txt delete mode 100644 src/docs/needless_late_init.txt delete mode 100644 src/docs/needless_lifetimes.txt delete mode 100644 src/docs/needless_match.txt delete mode 100644 src/docs/needless_option_as_deref.txt delete mode 100644 src/docs/needless_option_take.txt delete mode 100644 src/docs/needless_parens_on_range_literals.txt delete mode 100644 src/docs/needless_pass_by_value.txt delete mode 100644 src/docs/needless_question_mark.txt delete mode 100644 src/docs/needless_range_loop.txt delete mode 100644 src/docs/needless_return.txt delete mode 100644 src/docs/needless_splitn.txt delete mode 100644 src/docs/needless_update.txt delete mode 100644 src/docs/neg_cmp_op_on_partial_ord.txt delete mode 100644 src/docs/neg_multiply.txt delete mode 100644 src/docs/negative_feature_names.txt delete mode 100644 src/docs/never_loop.txt delete mode 100644 src/docs/new_ret_no_self.txt delete mode 100644 src/docs/new_without_default.txt delete mode 100644 src/docs/no_effect.txt delete mode 100644 src/docs/no_effect_replace.txt delete mode 100644 src/docs/no_effect_underscore_binding.txt delete mode 100644 src/docs/non_ascii_literal.txt delete mode 100644 src/docs/non_octal_unix_permissions.txt delete mode 100644 src/docs/non_send_fields_in_send_ty.txt delete mode 100644 src/docs/nonminimal_bool.txt delete mode 100644 src/docs/nonsensical_open_options.txt delete mode 100644 src/docs/nonstandard_macro_braces.txt delete mode 100644 src/docs/not_unsafe_ptr_arg_deref.txt delete mode 100644 src/docs/obfuscated_if_else.txt delete mode 100644 src/docs/octal_escapes.txt delete mode 100644 src/docs/ok_expect.txt delete mode 100644 src/docs/only_used_in_recursion.txt delete mode 100644 src/docs/op_ref.txt delete mode 100644 src/docs/option_as_ref_deref.txt delete mode 100644 src/docs/option_env_unwrap.txt delete mode 100644 src/docs/option_filter_map.txt delete mode 100644 src/docs/option_if_let_else.txt delete mode 100644 src/docs/option_map_or_none.txt delete mode 100644 src/docs/option_map_unit_fn.txt delete mode 100644 src/docs/option_option.txt delete mode 100644 src/docs/or_fun_call.txt delete mode 100644 src/docs/or_then_unwrap.txt delete mode 100644 src/docs/out_of_bounds_indexing.txt delete mode 100644 src/docs/overflow_check_conditional.txt delete mode 100644 src/docs/overly_complex_bool_expr.txt delete mode 100644 src/docs/panic.txt delete mode 100644 src/docs/panic_in_result_fn.txt delete mode 100644 src/docs/panicking_unwrap.txt delete mode 100644 src/docs/partial_pub_fields.txt delete mode 100644 src/docs/partialeq_ne_impl.txt delete mode 100644 src/docs/partialeq_to_none.txt delete mode 100644 src/docs/path_buf_push_overwrite.txt delete mode 100644 src/docs/pattern_type_mismatch.txt delete mode 100644 src/docs/possible_missing_comma.txt delete mode 100644 src/docs/precedence.txt delete mode 100644 src/docs/print_in_format_impl.txt delete mode 100644 src/docs/print_literal.txt delete mode 100644 src/docs/print_stderr.txt delete mode 100644 src/docs/print_stdout.txt delete mode 100644 src/docs/print_with_newline.txt delete mode 100644 src/docs/println_empty_string.txt delete mode 100644 src/docs/ptr_arg.txt delete mode 100644 src/docs/ptr_as_ptr.txt delete mode 100644 src/docs/ptr_eq.txt delete mode 100644 src/docs/ptr_offset_with_cast.txt delete mode 100644 src/docs/pub_use.txt delete mode 100644 src/docs/question_mark.txt delete mode 100644 src/docs/range_minus_one.txt delete mode 100644 src/docs/range_plus_one.txt delete mode 100644 src/docs/range_zip_with_len.txt delete mode 100644 src/docs/rc_buffer.txt delete mode 100644 src/docs/rc_clone_in_vec_init.txt delete mode 100644 src/docs/rc_mutex.txt delete mode 100644 src/docs/read_zero_byte_vec.txt delete mode 100644 src/docs/recursive_format_impl.txt delete mode 100644 src/docs/redundant_allocation.txt delete mode 100644 src/docs/redundant_clone.txt delete mode 100644 src/docs/redundant_closure.txt delete mode 100644 src/docs/redundant_closure_call.txt delete mode 100644 src/docs/redundant_closure_for_method_calls.txt delete mode 100644 src/docs/redundant_else.txt delete mode 100644 src/docs/redundant_feature_names.txt delete mode 100644 src/docs/redundant_field_names.txt delete mode 100644 src/docs/redundant_pattern.txt delete mode 100644 src/docs/redundant_pattern_matching.txt delete mode 100644 src/docs/redundant_pub_crate.txt delete mode 100644 src/docs/redundant_slicing.txt delete mode 100644 src/docs/redundant_static_lifetimes.txt delete mode 100644 src/docs/ref_binding_to_reference.txt delete mode 100644 src/docs/ref_option_ref.txt delete mode 100644 src/docs/repeat_once.txt delete mode 100644 src/docs/rest_pat_in_fully_bound_structs.txt delete mode 100644 src/docs/result_large_err.txt delete mode 100644 src/docs/result_map_or_into_option.txt delete mode 100644 src/docs/result_map_unit_fn.txt delete mode 100644 src/docs/result_unit_err.txt delete mode 100644 src/docs/return_self_not_must_use.txt delete mode 100644 src/docs/reversed_empty_ranges.txt delete mode 100644 src/docs/same_functions_in_if_condition.txt delete mode 100644 src/docs/same_item_push.txt delete mode 100644 src/docs/same_name_method.txt delete mode 100644 src/docs/search_is_some.txt delete mode 100644 src/docs/self_assignment.txt delete mode 100644 src/docs/self_named_constructors.txt delete mode 100644 src/docs/self_named_module_files.txt delete mode 100644 src/docs/semicolon_if_nothing_returned.txt delete mode 100644 src/docs/separated_literal_suffix.txt delete mode 100644 src/docs/serde_api_misuse.txt delete mode 100644 src/docs/shadow_reuse.txt delete mode 100644 src/docs/shadow_same.txt delete mode 100644 src/docs/shadow_unrelated.txt delete mode 100644 src/docs/short_circuit_statement.txt delete mode 100644 src/docs/should_implement_trait.txt delete mode 100644 src/docs/significant_drop_in_scrutinee.txt delete mode 100644 src/docs/similar_names.txt delete mode 100644 src/docs/single_char_add_str.txt delete mode 100644 src/docs/single_char_lifetime_names.txt delete mode 100644 src/docs/single_char_pattern.txt delete mode 100644 src/docs/single_component_path_imports.txt delete mode 100644 src/docs/single_element_loop.txt delete mode 100644 src/docs/single_match.txt delete mode 100644 src/docs/single_match_else.txt delete mode 100644 src/docs/size_of_in_element_count.txt delete mode 100644 src/docs/skip_while_next.txt delete mode 100644 src/docs/slow_vector_initialization.txt delete mode 100644 src/docs/stable_sort_primitive.txt delete mode 100644 src/docs/std_instead_of_alloc.txt delete mode 100644 src/docs/std_instead_of_core.txt delete mode 100644 src/docs/str_to_string.txt delete mode 100644 src/docs/string_add.txt delete mode 100644 src/docs/string_add_assign.txt delete mode 100644 src/docs/string_extend_chars.txt delete mode 100644 src/docs/string_from_utf8_as_bytes.txt delete mode 100644 src/docs/string_lit_as_bytes.txt delete mode 100644 src/docs/string_slice.txt delete mode 100644 src/docs/string_to_string.txt delete mode 100644 src/docs/strlen_on_c_strings.txt delete mode 100644 src/docs/struct_excessive_bools.txt delete mode 100644 src/docs/suboptimal_flops.txt delete mode 100644 src/docs/suspicious_arithmetic_impl.txt delete mode 100644 src/docs/suspicious_assignment_formatting.txt delete mode 100644 src/docs/suspicious_else_formatting.txt delete mode 100644 src/docs/suspicious_map.txt delete mode 100644 src/docs/suspicious_op_assign_impl.txt delete mode 100644 src/docs/suspicious_operation_groupings.txt delete mode 100644 src/docs/suspicious_splitn.txt delete mode 100644 src/docs/suspicious_to_owned.txt delete mode 100644 src/docs/suspicious_unary_op_formatting.txt delete mode 100644 src/docs/swap_ptr_to_ref.txt delete mode 100644 src/docs/tabs_in_doc_comments.txt delete mode 100644 src/docs/temporary_assignment.txt delete mode 100644 src/docs/to_digit_is_some.txt delete mode 100644 src/docs/to_string_in_format_args.txt delete mode 100644 src/docs/todo.txt delete mode 100644 src/docs/too_many_arguments.txt delete mode 100644 src/docs/too_many_lines.txt delete mode 100644 src/docs/toplevel_ref_arg.txt delete mode 100644 src/docs/trailing_empty_array.txt delete mode 100644 src/docs/trait_duplication_in_bounds.txt delete mode 100644 src/docs/transmute_bytes_to_str.txt delete mode 100644 src/docs/transmute_float_to_int.txt delete mode 100644 src/docs/transmute_int_to_bool.txt delete mode 100644 src/docs/transmute_int_to_char.txt delete mode 100644 src/docs/transmute_int_to_float.txt delete mode 100644 src/docs/transmute_num_to_bytes.txt delete mode 100644 src/docs/transmute_ptr_to_ptr.txt delete mode 100644 src/docs/transmute_ptr_to_ref.txt delete mode 100644 src/docs/transmute_undefined_repr.txt delete mode 100644 src/docs/transmutes_expressible_as_ptr_casts.txt delete mode 100644 src/docs/transmuting_null.txt delete mode 100644 src/docs/trim_split_whitespace.txt delete mode 100644 src/docs/trivial_regex.txt delete mode 100644 src/docs/trivially_copy_pass_by_ref.txt delete mode 100644 src/docs/try_err.txt delete mode 100644 src/docs/type_complexity.txt delete mode 100644 src/docs/type_repetition_in_bounds.txt delete mode 100644 src/docs/undocumented_unsafe_blocks.txt delete mode 100644 src/docs/undropped_manually_drops.txt delete mode 100644 src/docs/unicode_not_nfc.txt delete mode 100644 src/docs/unimplemented.txt delete mode 100644 src/docs/uninit_assumed_init.txt delete mode 100644 src/docs/uninit_vec.txt delete mode 100644 src/docs/uninlined_format_args.txt delete mode 100644 src/docs/unit_arg.txt delete mode 100644 src/docs/unit_cmp.txt delete mode 100644 src/docs/unit_hash.txt delete mode 100644 src/docs/unit_return_expecting_ord.txt delete mode 100644 src/docs/unnecessary_cast.txt delete mode 100644 src/docs/unnecessary_filter_map.txt delete mode 100644 src/docs/unnecessary_find_map.txt delete mode 100644 src/docs/unnecessary_fold.txt delete mode 100644 src/docs/unnecessary_join.txt delete mode 100644 src/docs/unnecessary_lazy_evaluations.txt delete mode 100644 src/docs/unnecessary_mut_passed.txt delete mode 100644 src/docs/unnecessary_operation.txt delete mode 100644 src/docs/unnecessary_owned_empty_strings.txt delete mode 100644 src/docs/unnecessary_self_imports.txt delete mode 100644 src/docs/unnecessary_sort_by.txt delete mode 100644 src/docs/unnecessary_to_owned.txt delete mode 100644 src/docs/unnecessary_unwrap.txt delete mode 100644 src/docs/unnecessary_wraps.txt delete mode 100644 src/docs/unneeded_field_pattern.txt delete mode 100644 src/docs/unneeded_wildcard_pattern.txt delete mode 100644 src/docs/unnested_or_patterns.txt delete mode 100644 src/docs/unreachable.txt delete mode 100644 src/docs/unreadable_literal.txt delete mode 100644 src/docs/unsafe_derive_deserialize.txt delete mode 100644 src/docs/unsafe_removed_from_name.txt delete mode 100644 src/docs/unseparated_literal_suffix.txt delete mode 100644 src/docs/unsound_collection_transmute.txt delete mode 100644 src/docs/unused_async.txt delete mode 100644 src/docs/unused_format_specs.txt delete mode 100644 src/docs/unused_io_amount.txt delete mode 100644 src/docs/unused_peekable.txt delete mode 100644 src/docs/unused_rounding.txt delete mode 100644 src/docs/unused_self.txt delete mode 100644 src/docs/unused_unit.txt delete mode 100644 src/docs/unusual_byte_groupings.txt delete mode 100644 src/docs/unwrap_in_result.txt delete mode 100644 src/docs/unwrap_or_else_default.txt delete mode 100644 src/docs/unwrap_used.txt delete mode 100644 src/docs/upper_case_acronyms.txt delete mode 100644 src/docs/use_debug.txt delete mode 100644 src/docs/use_self.txt delete mode 100644 src/docs/used_underscore_binding.txt delete mode 100644 src/docs/useless_asref.txt delete mode 100644 src/docs/useless_attribute.txt delete mode 100644 src/docs/useless_conversion.txt delete mode 100644 src/docs/useless_format.txt delete mode 100644 src/docs/useless_let_if_seq.txt delete mode 100644 src/docs/useless_transmute.txt delete mode 100644 src/docs/useless_vec.txt delete mode 100644 src/docs/vec_box.txt delete mode 100644 src/docs/vec_init_then_push.txt delete mode 100644 src/docs/vec_resize_to_zero.txt delete mode 100644 src/docs/verbose_bit_mask.txt delete mode 100644 src/docs/verbose_file_reads.txt delete mode 100644 src/docs/vtable_address_comparisons.txt delete mode 100644 src/docs/while_immutable_condition.txt delete mode 100644 src/docs/while_let_loop.txt delete mode 100644 src/docs/while_let_on_iterator.txt delete mode 100644 src/docs/wildcard_dependencies.txt delete mode 100644 src/docs/wildcard_enum_match_arm.txt delete mode 100644 src/docs/wildcard_imports.txt delete mode 100644 src/docs/wildcard_in_or_patterns.txt delete mode 100644 src/docs/write_literal.txt delete mode 100644 src/docs/write_with_newline.txt delete mode 100644 src/docs/writeln_empty_string.txt delete mode 100644 src/docs/wrong_self_convention.txt delete mode 100644 src/docs/wrong_transmute.txt delete mode 100644 src/docs/zero_divided_by_zero.txt delete mode 100644 src/docs/zero_prefixed_literal.txt delete mode 100644 src/docs/zero_ptr.txt delete mode 100644 src/docs/zero_sized_map_values.txt delete mode 100644 src/docs/zst_offset.txt create mode 100644 tests/ui-toml/mut_key/clippy.toml create mode 100644 tests/ui-toml/mut_key/mut_key.rs create mode 100644 tests/ui-toml/print_macro/clippy.toml create mode 100644 tests/ui-toml/print_macro/print_macro.rs create mode 100644 tests/ui-toml/print_macro/print_macro.stderr create mode 100644 tests/ui/crashes/ice-9746.rs create mode 100644 tests/ui/doc_unnecessary_unsafe.rs create mode 100644 tests/ui/doc_unnecessary_unsafe.stderr create mode 100644 tests/ui/from_raw_with_void_ptr.rs create mode 100644 tests/ui/from_raw_with_void_ptr.stderr delete mode 100644 tests/ui/let_underscore_drop.rs delete mode 100644 tests/ui/let_underscore_drop.stderr create mode 100644 tests/ui/let_underscore_future.rs create mode 100644 tests/ui/let_underscore_future.stderr create mode 100644 tests/ui/manual_is_ascii_check.fixed create mode 100644 tests/ui/manual_is_ascii_check.rs create mode 100644 tests/ui/manual_is_ascii_check.stderr create mode 100644 tests/ui/manual_let_else.rs create mode 100644 tests/ui/manual_let_else.stderr create mode 100644 tests/ui/manual_let_else_match.rs create mode 100644 tests/ui/manual_let_else_match.stderr create mode 100644 tests/ui/seek_from_current.fixed create mode 100644 tests/ui/seek_from_current.rs create mode 100644 tests/ui/seek_from_current.stderr create mode 100644 tests/ui/seek_to_start_instead_of_rewind.fixed create mode 100644 tests/ui/seek_to_start_instead_of_rewind.rs create mode 100644 tests/ui/seek_to_start_instead_of_rewind.stderr create mode 100644 tests/ui/suspicious_xor_used_as_pow.rs create mode 100644 tests/ui/suspicious_xor_used_as_pow.stderr create mode 100644 tests/ui/unchecked_duration_subtraction.fixed create mode 100644 tests/ui/unchecked_duration_subtraction.rs create mode 100644 tests/ui/unchecked_duration_subtraction.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d7bda27e4fc..6f1f73c1fd2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,157 @@ All notable changes to this project will be documented in this file. See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this document. -## Unreleased / In Rust Nightly +## Unreleased / Beta / In Rust Nightly -[3c7e7dbc...master](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...master) +[b52fb523...master](https://github.com/rust-lang/rust-clippy/compare/b52fb523...master) + +## Rust 1.65 + +Current stable, released 2022-11-03 + +[3c7e7dbc...b52fb523](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...b52fb523) + +### Important Changes + +* Clippy now has an `--explain ` command to show the lint description in the console + [#8952](https://github.com/rust-lang/rust-clippy/pull/8952) + +### New Lints + +* [`unused_peekable`] + [#9258](https://github.com/rust-lang/rust-clippy/pull/9258) +* [`collapsible_str_replace`] + [#9269](https://github.com/rust-lang/rust-clippy/pull/9269) +* [`manual_string_new`] + [#9295](https://github.com/rust-lang/rust-clippy/pull/9295) +* [`iter_on_empty_collections`] + [#9187](https://github.com/rust-lang/rust-clippy/pull/9187) +* [`iter_on_single_items`] + [#9187](https://github.com/rust-lang/rust-clippy/pull/9187) +* [`bool_to_int_with_if`] + [#9412](https://github.com/rust-lang/rust-clippy/pull/9412) +* [`multi_assignments`] + [#9379](https://github.com/rust-lang/rust-clippy/pull/9379) +* [`result_large_err`] + [#9373](https://github.com/rust-lang/rust-clippy/pull/9373) +* [`partialeq_to_none`] + [#9288](https://github.com/rust-lang/rust-clippy/pull/9288) +* [`suspicious_to_owned`] + [#8984](https://github.com/rust-lang/rust-clippy/pull/8984) +* [`cast_slice_from_raw_parts`] + [#9247](https://github.com/rust-lang/rust-clippy/pull/9247) +* [`manual_instant_elapsed`] + [#9264](https://github.com/rust-lang/rust-clippy/pull/9264) + +### Moves and Deprecations + +* Moved [`significant_drop_in_scrutinee`] to `nursery` (now allow-by-default) + [#9302](https://github.com/rust-lang/rust-clippy/pull/9302) +* Rename `logic_bug` to [`overly_complex_bool_expr`] + [#9306](https://github.com/rust-lang/rust-clippy/pull/9306) +* Rename `arithmetic` to [`arithmetic_side_effects`] + [#9443](https://github.com/rust-lang/rust-clippy/pull/9443) +* Moved [`only_used_in_recursion`] to complexity (now warn-by-default) + [#8804](https://github.com/rust-lang/rust-clippy/pull/8804) +* Moved [`assertions_on_result_states`] to restriction (now allow-by-default) + [#9273](https://github.com/rust-lang/rust-clippy/pull/9273) +* Renamed `blacklisted_name` to [`disallowed_names`] + [#8974](https://github.com/rust-lang/rust-clippy/pull/8974) + +### Enhancements + +* [`option_if_let_else`]: Now also checks for match expressions + [#8696](https://github.com/rust-lang/rust-clippy/pull/8696) +* [`explicit_auto_deref`]: Now lints on implicit returns in closures + [#9126](https://github.com/rust-lang/rust-clippy/pull/9126) +* [`needless_borrow`]: Now considers trait implementations + [#9136](https://github.com/rust-lang/rust-clippy/pull/9136) +* [`suboptimal_flops`], [`imprecise_flops`]: Now lint on constant expressions + [#9404](https://github.com/rust-lang/rust-clippy/pull/9404) +* [`if_let_mutex`]: Now detects mutex behind references and warns about deadlocks + [#9318](https://github.com/rust-lang/rust-clippy/pull/9318) + +### False Positive Fixes + +* [`unit_arg`] [`default_trait_access`] [`missing_docs_in_private_items`]: No longer + trigger in code generated from proc-macros + [#8694](https://github.com/rust-lang/rust-clippy/pull/8694) +* [`unwrap_used`]: Now lints uses of `unwrap_err` + [#9338](https://github.com/rust-lang/rust-clippy/pull/9338) +* [`expect_used`]: Now lints uses of `expect_err` + [#9338](https://github.com/rust-lang/rust-clippy/pull/9338) +* [`transmute_undefined_repr`]: Now longer lints if the first field is compatible + with the other type + [#9287](https://github.com/rust-lang/rust-clippy/pull/9287) +* [`unnecessary_to_owned`]: No longer lints, if type change cased errors in + the caller function + [#9424](https://github.com/rust-lang/rust-clippy/pull/9424) +* [`match_like_matches_macro`]: No longer lints, if there are comments inside the + match expression + [#9276](https://github.com/rust-lang/rust-clippy/pull/9276) +* [`partialeq_to_none`]: No longer trigger in code generated from macros + [#9389](https://github.com/rust-lang/rust-clippy/pull/9389) +* [`arithmetic_side_effects`]: No longer lints expressions that only use literals + [#9365](https://github.com/rust-lang/rust-clippy/pull/9365) +* [`explicit_auto_deref`]: Now ignores references on block expressions when the type + is `Sized`, on `dyn Trait` returns and when the suggestion is non-trivial + [#9126](https://github.com/rust-lang/rust-clippy/pull/9126) +* [`trait_duplication_in_bounds`]: Now better tracks bounds to avoid false positives + [#9167](https://github.com/rust-lang/rust-clippy/pull/9167) +* [`format_in_format_args`]: Now suggests cases where the result is formatted again + [#9349](https://github.com/rust-lang/rust-clippy/pull/9349) +* [`only_used_in_recursion`]: No longer lints on function without recursions and + takes external functions into account + [#8804](https://github.com/rust-lang/rust-clippy/pull/8804) +* [`missing_const_for_fn`]: No longer lints in proc-macros + [#9308](https://github.com/rust-lang/rust-clippy/pull/9308) +* [`non_ascii_literal`]: Allow non-ascii comments in tests and make sure `#[allow]` + attributes work in tests + [#9327](https://github.com/rust-lang/rust-clippy/pull/9327) +* [`question_mark`]: No longer lint `if let`s with subpatterns + [#9348](https://github.com/rust-lang/rust-clippy/pull/9348) +* [`needless_collect`]: No longer lints in loops + [#8992](https://github.com/rust-lang/rust-clippy/pull/8992) +* [`mut_mutex_lock`]: No longer lints if the mutex is behind an immutable reference + [#9418](https://github.com/rust-lang/rust-clippy/pull/9418) +* [`needless_return`]: Now ignores returns with arguments + [#9381](https://github.com/rust-lang/rust-clippy/pull/9381) +* [`range_plus_one`], [`range_minus_one`]: Now ignores code with macros + [#9446](https://github.com/rust-lang/rust-clippy/pull/9446) +* [`assertions_on_result_states`]: No longer lints on the unit type + [#9273](https://github.com/rust-lang/rust-clippy/pull/9273) + +### Suggestion Fixes/Improvements + +* [`unwrap_or_else_default`]: Now suggests `unwrap_or_default()` for empty strings + [#9421](https://github.com/rust-lang/rust-clippy/pull/9421) +* [`if_then_some_else_none`]: Now also suggests `bool::then_some` + [#9289](https://github.com/rust-lang/rust-clippy/pull/9289) +* [`redundant_closure_call`]: The suggestion now works for async closures + [#9053](https://github.com/rust-lang/rust-clippy/pull/9053) +* [`suboptimal_flops`]: Now suggests parenthesis when they are required + [#9394](https://github.com/rust-lang/rust-clippy/pull/9394) +* [`case_sensitive_file_extension_comparisons`]: Now suggests `map_or(..)` instead of `map(..).unwrap_or` + [#9341](https://github.com/rust-lang/rust-clippy/pull/9341) +* Deprecated configuration values can now be updated automatically + [#9252](https://github.com/rust-lang/rust-clippy/pull/9252) +* [`or_fun_call`]: Now suggest `Entry::or_default` for `Entry::or_insert(Default::default())` + [#9342](https://github.com/rust-lang/rust-clippy/pull/9342) +* [`unwrap_used`]: Only suggests `expect` if [`expect_used`] is allowed + [#9223](https://github.com/rust-lang/rust-clippy/pull/9223) + +### ICE Fixes + +* Fix ICE in [`useless_format`] for literals + [#9406](https://github.com/rust-lang/rust-clippy/pull/9406) +* Fix infinite loop in [`vec_init_then_push`] + [#9441](https://github.com/rust-lang/rust-clippy/pull/9441) +* Fix ICE when reading literals with weird proc-macro spans + [#9303](https://github.com/rust-lang/rust-clippy/pull/9303) ## Rust 1.64 -Current stable, released 2022-09-22 +Released 2022-09-22 [d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc) @@ -3903,6 +4047,7 @@ Released 2018-09-13 [`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into +[`from_raw_with_void_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_raw_with_void_ptr [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first @@ -3978,6 +4123,7 @@ Released 2018-09-13 [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop +[`let_underscore_future`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value @@ -3996,6 +4142,8 @@ Released 2018-09-13 [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed +[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check +[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive @@ -4198,6 +4346,8 @@ Released 2018-09-13 [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some +[`seek_from_current`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current +[`seek_to_start_instead_of_rewind`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_to_start_instead_of_rewind [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors [`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files @@ -4247,6 +4397,7 @@ Released 2018-09-13 [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn [`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting +[`suspicious_xor_used_as_pow`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_xor_used_as_pow [`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref [`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 @@ -4277,6 +4428,7 @@ Released 2018-09-13 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds +[`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc @@ -4298,6 +4450,7 @@ Released 2018-09-13 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings +[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index dec13e44a17f..e3708bc48539 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,70 +1,3 @@ # The Rust Code of Conduct -A version of this document [can be found online](https://www.rust-lang.org/conduct.html). - -## Conduct - -**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org) - -* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, - gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, - religion, nationality, or other similar characteristic. -* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and - welcoming environment for all. -* Please be kind and courteous. There's no need to be mean or rude. -* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and - numerous costs. There is seldom a right answer. -* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and - see how it works. -* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We - interpret the term "harassment" as including the definition in the Citizen - Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their - definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. -* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or - made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation - team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a - safe place for you and we've got your back. -* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. - -## Moderation - - -These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, -please contact the [Rust moderation team][mod_team]. - -1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, - are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) -2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. -3. Moderators will first respond to such remarks with a warning. -4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. -5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. -6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended - party a genuine apology. -7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a - different moderator, **in private**. Complaints about bans in-channel are not allowed. -8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate - situation, they should expect less leeway than others. - -In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically -unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly -if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can -drive people away from the community entirely. - -And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was -they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good -there was something you could've communicated better — remember that it's your responsibility to make your fellow -Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about -cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their -trust. - -The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, -#rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); -GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org -(users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the -maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider -explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. - -*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the -[Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* - -[mod_team]: https://www.rust-lang.org/team.html#Moderation-team +The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 85f94a74ad91..3158080d2b30 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,7 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [Issue and PR triage](#issue-and-pr-triage) - [Bors and Homu](#bors-and-homu) - [Contributions](#contributions) + - [License](#license) [Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy [Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct @@ -245,6 +246,38 @@ Contributions to Clippy should be made in the form of GitHub pull requests. Each be reviewed by a core contributor (someone with permission to land patches) and either landed in the main tree or given feedback for changes that would be required. +All PRs should include a `changelog` entry with a short comment explaining the change. The rule of thumb is basically, +"what do you believe is important from an outsider's perspective?" Often, PRs are only related to a single property of a +lint, and then it's good to mention that one. Otherwise, it's better to include too much detail than too little. + +Clippy's [changelog] is created from these comments. Every release, someone gets all commits from bors with a +`changelog: XYZ` entry and combines them into the changelog. This is a manual process. + +Examples: +- New lint + ``` + changelog: new lint: [`missing_trait_methods`] + ``` +- False positive fix + ``` + changelog: Fix [`unused_peekable`] false positive when peeked in a closure or called as `f(&mut peekable)` + ``` +- Purely internal change + ``` + changelog: none + ``` + +Note this it is fine for a PR to include multiple `changelog` entries, e.g.: +``` +changelog: Something 1 +changelog: Something 2 +changelog: Something 3 +``` + +[changelog]: CHANGELOG.md + +## License + All code in this repository is under the [Apache-2.0] or the [MIT] license. diff --git a/Cargo.toml b/Cargo.toml index 60200a88b858..fe425a2fb991 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.66" +version = "0.1.67" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/README.md b/README.md index a8a6b86d2a15..f74de7de42b8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Clippy -[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto) +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test%20(bors)/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto) [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license) A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. @@ -204,12 +204,6 @@ lints can be configured and the meaning of the variables. > > `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints. -> **Note** -> -> Configuration changes will not apply for code that has already been compiled and cached under `./target/`; -> for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure -> that any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch. - To deactivate the “for further information visit *lint-link*” message you can define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 2ac3b4fe2ed4..510c7e852af6 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -10,7 +10,6 @@ indoc = "1.0" itertools = "0.10.1" opener = "0.5" shell-escape = "0.1" -tempfile = "3.2" walkdir = "2.3" [features] diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index 71005449b4dd..aafd0f71a59b 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -36,20 +36,12 @@ pub fn run<'a>(path: &str, args: impl Iterator) { } else { exit_if_err(Command::new("cargo").arg("build").status()); - // Run in a tempdir as changes to clippy do not retrigger linting - let target = tempfile::Builder::new() - .prefix("clippy") - .tempdir() - .expect("failed to create tempdir"); - let status = Command::new(cargo_clippy_path()) .arg("clippy") .args(args) .current_dir(path) - .env("CARGO_TARGET_DIR", target.as_ref()) .status(); - target.close().expect("failed to remove tempdir"); exit_if_err(status); } } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index e690bc369cd4..837618c9294b 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -3,7 +3,7 @@ use aho_corasick::AhoCorasickBuilder; use indoc::writedoc; use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fmt::Write; use std::fs::{self, OpenOptions}; @@ -36,6 +36,60 @@ pub enum UpdateMode { pub fn update(update_mode: UpdateMode) { let (lints, deprecated_lints, renamed_lints) = gather_all(); generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); + remove_old_files(update_mode); +} + +/// Remove files no longer needed after +/// that may be reintroduced unintentionally +/// +/// FIXME: This is a temporary measure that should be removed when there are no more PRs that +/// include the stray files +fn remove_old_files(update_mode: UpdateMode) { + let mut failed = false; + let mut remove_file = |path: &Path| match update_mode { + UpdateMode::Check => { + if path.exists() { + failed = true; + println!("unexpected file: {}", path.display()); + } + }, + UpdateMode::Change => { + if fs::remove_file(path).is_ok() { + println!("removed file: {}", path.display()); + } + }, + }; + + let files = [ + "clippy_lints/src/lib.register_all.rs", + "clippy_lints/src/lib.register_cargo.rs", + "clippy_lints/src/lib.register_complexity.rs", + "clippy_lints/src/lib.register_correctness.rs", + "clippy_lints/src/lib.register_internal.rs", + "clippy_lints/src/lib.register_lints.rs", + "clippy_lints/src/lib.register_nursery.rs", + "clippy_lints/src/lib.register_pedantic.rs", + "clippy_lints/src/lib.register_perf.rs", + "clippy_lints/src/lib.register_restriction.rs", + "clippy_lints/src/lib.register_style.rs", + "clippy_lints/src/lib.register_suspicious.rs", + "src/docs.rs", + ]; + + for file in files { + remove_file(Path::new(file)); + } + + if let Ok(docs_dir) = fs::read_dir("src/docs") { + for doc_file in docs_dir { + let path = doc_file.unwrap().path(); + remove_file(&path); + } + } + + if failed { + exit_with_failure(); + } } fn generate_lint_files( @@ -104,9 +158,9 @@ fn generate_lint_files( ); process_file( - "clippy_lints/src/lib.register_lints.rs", + "clippy_lints/src/declared_lints.rs", update_mode, - &gen_register_lint_list(internal_lints.iter(), usable_lints.iter()), + &gen_declared_lints(internal_lints.iter(), usable_lints.iter()), ); process_file( "clippy_lints/src/lib.deprecated.rs", @@ -114,26 +168,6 @@ fn generate_lint_files( &gen_deprecated(deprecated_lints), ); - let all_group_lints = usable_lints.iter().filter(|l| { - matches!( - &*l.group, - "correctness" | "suspicious" | "style" | "complexity" | "perf" - ) - }); - let content = gen_lint_group_list("all", all_group_lints); - process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content); - - update_docs(update_mode, &usable_lints); - - for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { - let content = gen_lint_group_list(&lint_group, lints.iter()); - process_file( - format!("clippy_lints/src/lib.register_{lint_group}.rs"), - update_mode, - &content, - ); - } - let content = gen_deprecated_lints_test(deprecated_lints); process_file("tests/ui/deprecated.rs", update_mode, &content); @@ -141,62 +175,6 @@ fn generate_lint_files( process_file("tests/ui/rename.rs", update_mode, &content); } -fn update_docs(update_mode: UpdateMode, usable_lints: &[Lint]) { - replace_region_in_file(update_mode, Path::new("src/docs.rs"), "docs! {\n", "\n}\n", |res| { - for name in usable_lints.iter().map(|lint| lint.name.clone()).sorted() { - writeln!(res, r#" "{name}","#).unwrap(); - } - }); - - if update_mode == UpdateMode::Check { - let mut extra = BTreeSet::new(); - let mut lint_names = usable_lints - .iter() - .map(|lint| lint.name.clone()) - .collect::>(); - for file in std::fs::read_dir("src/docs").unwrap() { - let filename = file.unwrap().file_name().into_string().unwrap(); - if let Some(name) = filename.strip_suffix(".txt") { - if !lint_names.remove(name) { - extra.insert(name.to_string()); - } - } - } - - let failed = print_lint_names("extra lint docs:", &extra) | print_lint_names("missing lint docs:", &lint_names); - - if failed { - exit_with_failure(); - } - } else { - if std::fs::remove_dir_all("src/docs").is_err() { - eprintln!("could not remove src/docs directory"); - } - if std::fs::create_dir("src/docs").is_err() { - eprintln!("could not recreate src/docs directory"); - } - } - for lint in usable_lints { - process_file( - Path::new("src/docs").join(lint.name.clone() + ".txt"), - update_mode, - &lint.documentation, - ); - } -} - -fn print_lint_names(header: &str, lints: &BTreeSet) -> bool { - if lints.is_empty() { - return false; - } - println!("{header}"); - for lint in lints.iter().sorted() { - println!(" {lint}"); - } - println!(); - true -} - pub fn print_lints() { let (lint_list, _, _) = gather_all(); let usable_lints = Lint::usable_lints(&lint_list); @@ -641,26 +619,17 @@ struct Lint { desc: String, module: String, declaration_range: Range, - documentation: String, } impl Lint { #[must_use] - fn new( - name: &str, - group: &str, - desc: &str, - module: &str, - declaration_range: Range, - documentation: String, - ) -> Self { + fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range) -> Self { Self { name: name.to_lowercase(), group: group.into(), desc: remove_line_splices(desc), module: module.into(), declaration_range, - documentation, } } @@ -716,25 +685,6 @@ impl RenamedLint { } } -/// Generates the code for registering a group -fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator) -> String { - let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); - details.sort_unstable(); - - let mut output = GENERATED_FILE_COMMENT.to_string(); - - let _ = writeln!( - output, - "store.register_group(true, \"clippy::{group_name}\", Some(\"clippy_{group_name}\"), vec![", - ); - for (module, name) in details { - let _ = writeln!(output, " LintId::of({module}::{name}),"); - } - output.push_str("])\n"); - - output -} - /// Generates the `register_removed` code #[must_use] fn gen_deprecated(lints: &[DeprecatedLint]) -> String { @@ -759,7 +709,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String { /// Generates the code for registering lints #[must_use] -fn gen_register_lint_list<'a>( +fn gen_declared_lints<'a>( internal_lints: impl Iterator, usable_lints: impl Iterator, ) -> String { @@ -770,15 +720,15 @@ fn gen_register_lint_list<'a>( details.sort_unstable(); let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str("store.register_lints(&[\n"); + output.push_str("pub(crate) static LINTS: &[&crate::LintInfo] = &[\n"); for (is_public, module_name, lint_name) in details { if !is_public { output.push_str(" #[cfg(feature = \"internal\")]\n"); } - let _ = writeln!(output, " {module_name}::{lint_name},"); + let _ = writeln!(output, " crate::{module_name}::{lint_name}_INFO,"); } - output.push_str("])\n"); + output.push_str("];\n"); output } @@ -910,35 +860,26 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint", ) { let start = range.start; - let mut docs = String::with_capacity(128); - let mut iter = iter.by_ref().filter(|t| !matches!(t.token_kind, TokenKind::Whitespace)); + let mut iter = iter + .by_ref() + .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); // matches `!{` match_tokens!(iter, Bang OpenBrace); - let mut in_code = false; - while let Some(t) = iter.next() { - match t.token_kind { - TokenKind::LineComment { .. } => { - if let Some(line) = t.content.strip_prefix("/// ").or_else(|| t.content.strip_prefix("///")) { - if line.starts_with("```") { - docs += "```\n"; - in_code = !in_code; - } else if !(in_code && line.starts_with("# ")) { - docs += line; - docs.push('\n'); - } - } - }, - TokenKind::Pound => { - match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); - break; - }, - TokenKind::Ident => { - break; - }, - _ => {}, - } + match iter.next() { + // #[clippy::version = "version"] pub + Some(LintDeclSearchResult { + token_kind: TokenKind::Pound, + .. + }) => { + match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); + }, + // pub + Some(LintDeclSearchResult { + token_kind: TokenKind::Ident, + .. + }) => (), + _ => continue, } - docs.pop(); // remove final newline let (name, group, desc) = match_tokens!( iter, @@ -956,7 +897,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { .. }) = iter.next() { - lints.push(Lint::new(name, group, desc, module, start..range.end, docs)); + lints.push(Lint::new(name, group, desc, module, start..range.end)); } } } @@ -1186,7 +1127,6 @@ mod tests { "\"really long text\"", "module_name", Range::default(), - String::new(), ), Lint::new( "doc_markdown", @@ -1194,7 +1134,6 @@ mod tests { "\"single line\"", "module_name", Range::default(), - String::new(), ), ]; assert_eq!(expected, result); @@ -1234,7 +1173,6 @@ mod tests { "\"abc\"", "module_name", Range::default(), - String::new(), ), Lint::new( "should_assert_eq2", @@ -1242,7 +1180,6 @@ mod tests { "\"abc\"", "module_name", Range::default(), - String::new(), ), Lint::new( "should_assert_eq2", @@ -1250,7 +1187,6 @@ mod tests { "\"abc\"", "module_name", Range::default(), - String::new(), ), ]; let expected = vec![Lint::new( @@ -1259,7 +1195,6 @@ mod tests { "\"abc\"", "module_name", Range::default(), - String::new(), )]; assert_eq!(expected, Lint::usable_lints(&lints)); } @@ -1267,51 +1202,22 @@ mod tests { #[test] fn test_by_lint_group() { let lints = vec![ - Lint::new( - "should_assert_eq", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), Lint::new( "should_assert_eq2", "group2", "\"abc\"", "module_name", Range::default(), - String::new(), - ), - Lint::new( - "incorrect_match", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), ), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), ]; let mut expected: HashMap> = HashMap::new(); expected.insert( "group1".to_string(), vec![ - Lint::new( - "should_assert_eq", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), - Lint::new( - "incorrect_match", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), ], ); expected.insert( @@ -1322,7 +1228,6 @@ mod tests { "\"abc\"", "module_name", Range::default(), - String::new(), )], ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); @@ -1357,48 +1262,4 @@ mod tests { assert_eq!(expected, gen_deprecated(&lints)); } - - #[test] - fn test_gen_lint_group_list() { - let lints = vec![ - Lint::new( - "abc", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), - Lint::new( - "should_assert_eq", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), - Lint::new( - "internal", - "internal_style", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), - ]; - let expected = GENERATED_FILE_COMMENT.to_string() - + &[ - "store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![", - " LintId::of(module_name::ABC),", - " LintId::of(module_name::INTERNAL),", - " LintId::of(module_name::SHOULD_ASSERT_EQ),", - "])", - ] - .join("\n") - + "\n"; - - let result = gen_lint_group_list("group1", lints.iter()); - - assert_eq!(expected, result); - } } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 6fbd6401ef3e..aedff24c12c6 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.66" +version = "0.1.67" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -11,6 +11,7 @@ edition = "2021" [dependencies] cargo_metadata = "0.14" clippy_utils = { path = "../clippy_utils" } +declare_clippy_lint = { path = "../declare_clippy_lint" } if_chain = "1.0" itertools = "0.10.1" pulldown-cmark = { version = "0.9", default-features = false } diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0bd1f8b784e8..ecf8e83375db 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -11,14 +11,14 @@ use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, }; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_span::{sym, DUMMY_SP}; use semver::Version; static UNIX_SYSTEMS: &[&str] = &[ @@ -303,6 +303,26 @@ declare_lint_pass!(Attributes => [ ]); impl<'tcx> LateLintPass<'tcx> for Attributes { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + for (name, level) in &cx.sess().opts.lint_opts { + if name == "clippy::restriction" && *level > Level::Allow { + span_lint_and_then( + cx, + BLANKET_CLIPPY_RESTRICTION_LINTS, + DUMMY_SP, + "`clippy::restriction` is not meant to be enabled as a group", + |diag| { + diag.note(format!( + "because of the command line `--{} clippy::restriction`", + level.as_str() + )); + diag.help("enable the restriction lints you need individually"); + }, + ); + } + } + } + fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { if let Some(items) = &attr.meta_item_list() { if let Some(ident) = attr.ident() { @@ -358,7 +378,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { | "enum_glob_use" | "redundant_pub_crate" | "macro_use_imports" - | "unsafe_removed_from_name", + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" ) }) { @@ -441,9 +463,9 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe cx, BLANKET_CLIPPY_RESTRICTION_LINTS, lint.span(), - "restriction lints are not meant to be all enabled", + "`clippy::restriction` is not meant to be enabled as a group", None, - "try enabling only the lints you really need", + "enable the restriction lints you need individually", ); } } @@ -464,6 +486,11 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem return; } + // Check if the attribute is in an external macro and therefore out of the developer's control + if in_external_macro(cx.sess(), attr.span) { + return; + } + span_lint_and_help( cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 34717811866d..d40a385435af 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -189,7 +188,7 @@ impl LateLintPass<'_> for AwaitHolding { fn check_crate(&mut self, cx: &LateContext<'_>) { for conf in &self.conf_invalid_types { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.def_ids.insert(id, conf.clone()); } } diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 001d74c26054..bdb3a0116027 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -1,9 +1,10 @@ +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 clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, is_integer_literal, sugg::Sugg}; +use clippy_utils::{diagnostics::span_lint_and_then, in_constant, is_else_clause, is_integer_literal, sugg::Sugg}; use rustc_errors::Applicability; declare_clippy_lint! { @@ -12,7 +13,7 @@ declare_clippy_lint! { /// this lint suggests using a `from()` function or an `as` coercion. /// /// ### Why is this bad? - /// Coercion or `from()` is idiomatic way to convert bool to a number. + /// Coercion or `from()` is another way to convert bool to a number. /// Both methods are guaranteed to return 1 for true, and 0 for false. /// /// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E @@ -38,23 +39,23 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.65.0"] pub BOOL_TO_INT_WITH_IF, - style, + pedantic, "using if to convert bool to int" } declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { - fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if !expr.span.from_expansion() { - check_if_else(ctx, expr); + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) { + check_if_else(cx, expr); } } } -fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if let ExprKind::If(check, then, Some(else_)) = expr.kind +fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if let Some(If { cond, then, r#else: Some(r#else) }) = If::hir(expr) && let Some(then_lit) = int_literal(then) - && let Some(else_lit) = int_literal(else_) + && let Some(else_lit) = int_literal(r#else) { let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) { false @@ -66,17 +67,17 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx }; let mut applicability = Applicability::MachineApplicable; let snippet = { - let mut sugg = Sugg::hir_with_applicability(ctx, check, "..", &mut applicability); + let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); if inverted { sugg = !sugg; } sugg }; - let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type + let ty = cx.typeck_results().expr_ty(then_lit); // then and else must be of same type let suggestion = { - let wrap_in_curly = is_else_clause(ctx.tcx, expr); + let wrap_in_curly = is_else_clause(cx.tcx, expr); let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into()); if wrap_in_curly { s = s.blockify(); @@ -87,7 +88,7 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx let into_snippet = snippet.clone().maybe_par(); let as_snippet = snippet.as_ty(ty); - span_lint_and_then(ctx, + span_lint_and_then(cx, BOOL_TO_INT_WITH_IF, expr.span, "boolean to int conversion using if", diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 08164c0b654e..939bdbcdc7cd 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -481,7 +481,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { } } -fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { +fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); cx.tcx .get_diagnostic_item(sym::Ord) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index b72c4c772f1c..7148b5e6ebf4 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -593,7 +593,7 @@ declare_clippy_lint! { /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); /// ``` /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub CAST_SLICE_FROM_RAW_PARTS, suspicious, "casting a slice created from a pointer and length to a slice pointer" diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 77af3b53d633..1c3a89a97824 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -4,11 +4,11 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::LimitStack; +use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack}; use core::ops::ControlFlow; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -56,15 +56,13 @@ impl CognitiveComplexity { cx: &LateContext<'tcx>, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, - body: &'tcx Body<'_>, + expr: &'tcx Expr<'_>, body_span: Span, ) { if body_span.from_expansion() { return; } - let expr = body.value; - let mut cc = 1u64; let mut returns = 0u64; let _: Option = for_each_expr(expr, |e| { @@ -146,7 +144,18 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { ) { let def_id = cx.tcx.hir().local_def_id(hir_id); if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) { - self.check(cx, kind, decl, body, span); + let expr = if is_async_fn(kind) { + match get_async_fn_body(cx.tcx, body) { + Some(b) => b, + None => { + return; + }, + } + } else { + body.value + }; + + self.check(cx, kind, decl, expr, span); } } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs new file mode 100644 index 000000000000..0d3fc43a6443 --- /dev/null +++ b/clippy_lints/src/declared_lints.rs @@ -0,0 +1,628 @@ +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + +pub(crate) static LINTS: &[&crate::LintInfo] = &[ + #[cfg(feature = "internal")] + crate::utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::if_chain_style::IF_CHAIN_STYLE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, + crate::almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE_INFO, + crate::approx_const::APPROX_CONSTANT_INFO, + crate::as_conversions::AS_CONVERSIONS_INFO, + crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO, + crate::asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX_INFO, + crate::assertions_on_constants::ASSERTIONS_ON_CONSTANTS_INFO, + crate::assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES_INFO, + crate::async_yields_async::ASYNC_YIELDS_ASYNC_INFO, + crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO, + crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO, + crate::attrs::DEPRECATED_CFG_ATTR_INFO, + crate::attrs::DEPRECATED_SEMVER_INFO, + crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, + crate::attrs::INLINE_ALWAYS_INFO, + crate::attrs::MISMATCHED_TARGET_OS_INFO, + crate::attrs::USELESS_ATTRIBUTE_INFO, + crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO, + crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO, + crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO, + crate::blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS_INFO, + crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO, + crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO, + crate::booleans::NONMINIMAL_BOOL_INFO, + crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO, + crate::borrow_deref_ref::BORROW_DEREF_REF_INFO, + crate::box_default::BOX_DEFAULT_INFO, + crate::cargo::CARGO_COMMON_METADATA_INFO, + crate::cargo::MULTIPLE_CRATE_VERSIONS_INFO, + crate::cargo::NEGATIVE_FEATURE_NAMES_INFO, + crate::cargo::REDUNDANT_FEATURE_NAMES_INFO, + crate::cargo::WILDCARD_DEPENDENCIES_INFO, + crate::casts::AS_PTR_CAST_MUT_INFO, + crate::casts::AS_UNDERSCORE_INFO, + crate::casts::BORROW_AS_PTR_INFO, + crate::casts::CAST_ABS_TO_UNSIGNED_INFO, + crate::casts::CAST_ENUM_CONSTRUCTOR_INFO, + crate::casts::CAST_ENUM_TRUNCATION_INFO, + crate::casts::CAST_LOSSLESS_INFO, + crate::casts::CAST_NAN_TO_INT_INFO, + crate::casts::CAST_POSSIBLE_TRUNCATION_INFO, + crate::casts::CAST_POSSIBLE_WRAP_INFO, + crate::casts::CAST_PRECISION_LOSS_INFO, + crate::casts::CAST_PTR_ALIGNMENT_INFO, + crate::casts::CAST_REF_TO_MUT_INFO, + crate::casts::CAST_SIGN_LOSS_INFO, + crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO, + crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO, + crate::casts::CHAR_LIT_AS_U8_INFO, + crate::casts::FN_TO_NUMERIC_CAST_INFO, + crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, + crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, + crate::casts::PTR_AS_PTR_INFO, + crate::casts::UNNECESSARY_CAST_INFO, + crate::checked_conversions::CHECKED_CONVERSIONS_INFO, + crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, + crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, + crate::collapsible_if::COLLAPSIBLE_IF_INFO, + crate::comparison_chain::COMPARISON_CHAIN_INFO, + crate::copies::BRANCHES_SHARING_CODE_INFO, + crate::copies::IFS_SAME_COND_INFO, + crate::copies::IF_SAME_THEN_ELSE_INFO, + crate::copies::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, + crate::copy_iterator::COPY_ITERATOR_INFO, + crate::crate_in_macro_def::CRATE_IN_MACRO_DEF_INFO, + crate::create_dir::CREATE_DIR_INFO, + crate::dbg_macro::DBG_MACRO_INFO, + crate::default::DEFAULT_TRAIT_ACCESS_INFO, + crate::default::FIELD_REASSIGN_WITH_DEFAULT_INFO, + crate::default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY_INFO, + crate::default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK_INFO, + crate::default_union_representation::DEFAULT_UNION_REPRESENTATION_INFO, + crate::dereference::EXPLICIT_AUTO_DEREF_INFO, + crate::dereference::EXPLICIT_DEREF_METHODS_INFO, + crate::dereference::NEEDLESS_BORROW_INFO, + crate::dereference::REF_BINDING_TO_REFERENCE_INFO, + crate::derivable_impls::DERIVABLE_IMPLS_INFO, + crate::derive::DERIVE_HASH_XOR_EQ_INFO, + crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO, + crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO, + crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO, + crate::derive::UNSAFE_DERIVE_DESERIALIZE_INFO, + crate::disallowed_macros::DISALLOWED_MACROS_INFO, + crate::disallowed_methods::DISALLOWED_METHODS_INFO, + crate::disallowed_names::DISALLOWED_NAMES_INFO, + crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, + crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_LINK_WITH_QUOTES_INFO, + crate::doc::DOC_MARKDOWN_INFO, + crate::doc::MISSING_ERRORS_DOC_INFO, + crate::doc::MISSING_PANICS_DOC_INFO, + crate::doc::MISSING_SAFETY_DOC_INFO, + crate::doc::NEEDLESS_DOCTEST_MAIN_INFO, + crate::doc::UNNECESSARY_SAFETY_DOC_INFO, + crate::double_parens::DOUBLE_PARENS_INFO, + crate::drop_forget_ref::DROP_COPY_INFO, + crate::drop_forget_ref::DROP_NON_DROP_INFO, + crate::drop_forget_ref::DROP_REF_INFO, + crate::drop_forget_ref::FORGET_COPY_INFO, + crate::drop_forget_ref::FORGET_NON_DROP_INFO, + crate::drop_forget_ref::FORGET_REF_INFO, + crate::drop_forget_ref::UNDROPPED_MANUALLY_DROPS_INFO, + crate::duplicate_mod::DUPLICATE_MOD_INFO, + crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO, + crate::empty_drop::EMPTY_DROP_INFO, + crate::empty_enum::EMPTY_ENUM_INFO, + crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO, + crate::entry::MAP_ENTRY_INFO, + crate::enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT_INFO, + crate::enum_variants::ENUM_VARIANT_NAMES_INFO, + crate::enum_variants::MODULE_INCEPTION_INFO, + crate::enum_variants::MODULE_NAME_REPETITIONS_INFO, + crate::equatable_if_let::EQUATABLE_IF_LET_INFO, + crate::escape::BOXED_LOCAL_INFO, + crate::eta_reduction::REDUNDANT_CLOSURE_INFO, + crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO, + crate::excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS_INFO, + crate::excessive_bools::STRUCT_EXCESSIVE_BOOLS_INFO, + crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO, + crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO, + crate::exit::EXIT_INFO, + crate::explicit_write::EXPLICIT_WRITE_INFO, + crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO, + crate::float_literal::EXCESSIVE_PRECISION_INFO, + crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, + crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO, + crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO, + crate::format::USELESS_FORMAT_INFO, + crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO, + crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO, + crate::format_args::UNINLINED_FORMAT_ARGS_INFO, + crate::format_args::UNUSED_FORMAT_SPECS_INFO, + crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO, + crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO, + crate::format_push_string::FORMAT_PUSH_STRING_INFO, + crate::formatting::POSSIBLE_MISSING_COMMA_INFO, + crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO, + crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO, + crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO, + crate::from_over_into::FROM_OVER_INTO_INFO, + crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO, + crate::from_str_radix_10::FROM_STR_RADIX_10_INFO, + crate::functions::DOUBLE_MUST_USE_INFO, + crate::functions::MUST_USE_CANDIDATE_INFO, + crate::functions::MUST_USE_UNIT_INFO, + crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, + crate::functions::RESULT_LARGE_ERR_INFO, + crate::functions::RESULT_UNIT_ERR_INFO, + crate::functions::TOO_MANY_ARGUMENTS_INFO, + crate::functions::TOO_MANY_LINES_INFO, + crate::future_not_send::FUTURE_NOT_SEND_INFO, + crate::if_let_mutex::IF_LET_MUTEX_INFO, + crate::if_not_else::IF_NOT_ELSE_INFO, + crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, + crate::implicit_hasher::IMPLICIT_HASHER_INFO, + crate::implicit_return::IMPLICIT_RETURN_INFO, + crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO, + crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, + crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, + crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO, + crate::indexing_slicing::INDEXING_SLICING_INFO, + crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO, + crate::infinite_iter::INFINITE_ITER_INFO, + crate::infinite_iter::MAYBE_INFINITE_ITER_INFO, + crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO, + crate::inherent_to_string::INHERENT_TO_STRING_INFO, + crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, + crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, + crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, + crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, + crate::int_plus_one::INT_PLUS_ONE_INFO, + crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, + crate::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_INFO, + crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO, + crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO, + crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO, + crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO, + crate::large_include_file::LARGE_INCLUDE_FILE_INFO, + crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO, + crate::len_zero::COMPARISON_TO_EMPTY_INFO, + crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO, + crate::len_zero::LEN_ZERO_INFO, + crate::let_if_seq::USELESS_LET_IF_SEQ_INFO, + crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO, + crate::let_underscore::LET_UNDERSCORE_LOCK_INFO, + crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO, + crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, + crate::lifetimes::NEEDLESS_LIFETIMES_INFO, + crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, + crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO, + crate::literal_representation::LARGE_DIGIT_GROUPS_INFO, + crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO, + crate::literal_representation::UNREADABLE_LITERAL_INFO, + crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, + crate::loops::EMPTY_LOOP_INFO, + crate::loops::EXPLICIT_COUNTER_LOOP_INFO, + crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, + crate::loops::EXPLICIT_ITER_LOOP_INFO, + crate::loops::FOR_KV_MAP_INFO, + crate::loops::ITER_NEXT_LOOP_INFO, + crate::loops::MANUAL_FIND_INFO, + crate::loops::MANUAL_FLATTEN_INFO, + crate::loops::MANUAL_MEMCPY_INFO, + crate::loops::MISSING_SPIN_LOOP_INFO, + crate::loops::MUT_RANGE_BOUND_INFO, + crate::loops::NEEDLESS_RANGE_LOOP_INFO, + crate::loops::NEVER_LOOP_INFO, + crate::loops::SAME_ITEM_PUSH_INFO, + crate::loops::SINGLE_ELEMENT_LOOP_INFO, + crate::loops::WHILE_IMMUTABLE_CONDITION_INFO, + crate::loops::WHILE_LET_LOOP_INFO, + crate::loops::WHILE_LET_ON_ITERATOR_INFO, + crate::macro_use::MACRO_USE_IMPORTS_INFO, + crate::main_recursion::MAIN_RECURSION_INFO, + crate::manual_assert::MANUAL_ASSERT_INFO, + crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, + crate::manual_bits::MANUAL_BITS_INFO, + crate::manual_clamp::MANUAL_CLAMP_INFO, + crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, + crate::manual_let_else::MANUAL_LET_ELSE_INFO, + crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, + crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO, + crate::manual_retain::MANUAL_RETAIN_INFO, + crate::manual_string_new::MANUAL_STRING_NEW_INFO, + crate::manual_strip::MANUAL_STRIP_INFO, + crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO, + crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO, + crate::match_result_ok::MATCH_RESULT_OK_INFO, + crate::matches::COLLAPSIBLE_MATCH_INFO, + crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO, + crate::matches::MANUAL_FILTER_INFO, + crate::matches::MANUAL_MAP_INFO, + crate::matches::MANUAL_UNWRAP_OR_INFO, + crate::matches::MATCH_AS_REF_INFO, + crate::matches::MATCH_BOOL_INFO, + crate::matches::MATCH_LIKE_MATCHES_MACRO_INFO, + crate::matches::MATCH_ON_VEC_ITEMS_INFO, + crate::matches::MATCH_OVERLAPPING_ARM_INFO, + crate::matches::MATCH_REF_PATS_INFO, + crate::matches::MATCH_SAME_ARMS_INFO, + crate::matches::MATCH_SINGLE_BINDING_INFO, + crate::matches::MATCH_STR_CASE_MISMATCH_INFO, + crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO, + crate::matches::MATCH_WILD_ERR_ARM_INFO, + crate::matches::NEEDLESS_MATCH_INFO, + crate::matches::REDUNDANT_PATTERN_MATCHING_INFO, + crate::matches::REST_PAT_IN_FULLY_BOUND_STRUCTS_INFO, + crate::matches::SIGNIFICANT_DROP_IN_SCRUTINEE_INFO, + crate::matches::SINGLE_MATCH_INFO, + crate::matches::SINGLE_MATCH_ELSE_INFO, + crate::matches::TRY_ERR_INFO, + crate::matches::WILDCARD_ENUM_MATCH_ARM_INFO, + crate::matches::WILDCARD_IN_OR_PATTERNS_INFO, + crate::mem_forget::MEM_FORGET_INFO, + crate::mem_replace::MEM_REPLACE_OPTION_WITH_NONE_INFO, + crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO, + crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO, + crate::methods::BIND_INSTEAD_OF_MAP_INFO, + crate::methods::BYTES_COUNT_TO_LEN_INFO, + crate::methods::BYTES_NTH_INFO, + crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO, + crate::methods::CHARS_LAST_CMP_INFO, + crate::methods::CHARS_NEXT_CMP_INFO, + crate::methods::CLONED_INSTEAD_OF_COPIED_INFO, + crate::methods::CLONE_DOUBLE_REF_INFO, + crate::methods::CLONE_ON_COPY_INFO, + crate::methods::CLONE_ON_REF_PTR_INFO, + crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, + crate::methods::ERR_EXPECT_INFO, + crate::methods::EXPECT_FUN_CALL_INFO, + crate::methods::EXPECT_USED_INFO, + crate::methods::EXTEND_WITH_DRAIN_INFO, + crate::methods::FILETYPE_IS_FILE_INFO, + crate::methods::FILTER_MAP_IDENTITY_INFO, + crate::methods::FILTER_MAP_NEXT_INFO, + crate::methods::FILTER_NEXT_INFO, + crate::methods::FLAT_MAP_IDENTITY_INFO, + crate::methods::FLAT_MAP_OPTION_INFO, + crate::methods::FROM_ITER_INSTEAD_OF_COLLECT_INFO, + crate::methods::GET_FIRST_INFO, + crate::methods::GET_LAST_WITH_LEN_INFO, + crate::methods::GET_UNWRAP_INFO, + crate::methods::IMPLICIT_CLONE_INFO, + crate::methods::INEFFICIENT_TO_STRING_INFO, + crate::methods::INSPECT_FOR_EACH_INFO, + crate::methods::INTO_ITER_ON_REF_INFO, + crate::methods::IS_DIGIT_ASCII_RADIX_INFO, + crate::methods::ITERATOR_STEP_BY_ZERO_INFO, + crate::methods::ITER_CLONED_COLLECT_INFO, + crate::methods::ITER_COUNT_INFO, + crate::methods::ITER_KV_MAP_INFO, + crate::methods::ITER_NEXT_SLICE_INFO, + crate::methods::ITER_NTH_INFO, + crate::methods::ITER_NTH_ZERO_INFO, + crate::methods::ITER_ON_EMPTY_COLLECTIONS_INFO, + crate::methods::ITER_ON_SINGLE_ITEMS_INFO, + crate::methods::ITER_OVEREAGER_CLONED_INFO, + crate::methods::ITER_SKIP_NEXT_INFO, + crate::methods::ITER_WITH_DRAIN_INFO, + crate::methods::MANUAL_FILTER_MAP_INFO, + crate::methods::MANUAL_FIND_MAP_INFO, + crate::methods::MANUAL_OK_OR_INFO, + crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO, + crate::methods::MANUAL_SPLIT_ONCE_INFO, + crate::methods::MANUAL_STR_REPEAT_INFO, + crate::methods::MAP_CLONE_INFO, + crate::methods::MAP_COLLECT_RESULT_UNIT_INFO, + crate::methods::MAP_ERR_IGNORE_INFO, + crate::methods::MAP_FLATTEN_INFO, + crate::methods::MAP_IDENTITY_INFO, + crate::methods::MAP_UNWRAP_OR_INFO, + crate::methods::MUT_MUTEX_LOCK_INFO, + crate::methods::NAIVE_BYTECOUNT_INFO, + crate::methods::NEEDLESS_COLLECT_INFO, + crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO, + crate::methods::NEEDLESS_OPTION_TAKE_INFO, + crate::methods::NEEDLESS_SPLITN_INFO, + crate::methods::NEW_RET_NO_SELF_INFO, + crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO, + crate::methods::NO_EFFECT_REPLACE_INFO, + crate::methods::OBFUSCATED_IF_ELSE_INFO, + crate::methods::OK_EXPECT_INFO, + crate::methods::OPTION_AS_REF_DEREF_INFO, + crate::methods::OPTION_FILTER_MAP_INFO, + crate::methods::OPTION_MAP_OR_NONE_INFO, + crate::methods::OR_FUN_CALL_INFO, + crate::methods::OR_THEN_UNWRAP_INFO, + crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, + crate::methods::RANGE_ZIP_WITH_LEN_INFO, + crate::methods::REPEAT_ONCE_INFO, + crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, + crate::methods::SEARCH_IS_SOME_INFO, + crate::methods::SEEK_FROM_CURRENT_INFO, + crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO, + crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO, + crate::methods::SINGLE_CHAR_ADD_STR_INFO, + crate::methods::SINGLE_CHAR_PATTERN_INFO, + crate::methods::SKIP_WHILE_NEXT_INFO, + crate::methods::STABLE_SORT_PRIMITIVE_INFO, + crate::methods::STRING_EXTEND_CHARS_INFO, + crate::methods::SUSPICIOUS_MAP_INFO, + crate::methods::SUSPICIOUS_SPLITN_INFO, + crate::methods::SUSPICIOUS_TO_OWNED_INFO, + crate::methods::UNINIT_ASSUMED_INIT_INFO, + crate::methods::UNIT_HASH_INFO, + crate::methods::UNNECESSARY_FILTER_MAP_INFO, + crate::methods::UNNECESSARY_FIND_MAP_INFO, + crate::methods::UNNECESSARY_FOLD_INFO, + crate::methods::UNNECESSARY_JOIN_INFO, + crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO, + crate::methods::UNNECESSARY_SORT_BY_INFO, + crate::methods::UNNECESSARY_TO_OWNED_INFO, + crate::methods::UNWRAP_OR_ELSE_DEFAULT_INFO, + crate::methods::UNWRAP_USED_INFO, + crate::methods::USELESS_ASREF_INFO, + crate::methods::VEC_RESIZE_TO_ZERO_INFO, + crate::methods::VERBOSE_FILE_READS_INFO, + crate::methods::WRONG_SELF_CONVENTION_INFO, + crate::methods::ZST_OFFSET_INFO, + crate::minmax::MIN_MAX_INFO, + crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, + crate::misc::TOPLEVEL_REF_ARG_INFO, + crate::misc::USED_UNDERSCORE_BINDING_INFO, + crate::misc::ZERO_PTR_INFO, + crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, + crate::misc_early::DOUBLE_NEG_INFO, + crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO, + crate::misc_early::MIXED_CASE_HEX_LITERALS_INFO, + crate::misc_early::REDUNDANT_PATTERN_INFO, + crate::misc_early::SEPARATED_LITERAL_SUFFIX_INFO, + crate::misc_early::UNNEEDED_FIELD_PATTERN_INFO, + crate::misc_early::UNNEEDED_WILDCARD_PATTERN_INFO, + crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO, + crate::misc_early::ZERO_PREFIXED_LITERAL_INFO, + crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO, + crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO, + crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO, + crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO, + crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO, + crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO, + crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO, + crate::mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION_INFO, + crate::module_style::MOD_MODULE_FILES_INFO, + crate::module_style::SELF_NAMED_MODULE_FILES_INFO, + crate::multi_assignments::MULTI_ASSIGNMENTS_INFO, + crate::mut_key::MUTABLE_KEY_TYPE_INFO, + crate::mut_mut::MUT_MUT_INFO, + crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO, + crate::mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL_INFO, + crate::mutex_atomic::MUTEX_ATOMIC_INFO, + crate::mutex_atomic::MUTEX_INTEGER_INFO, + crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO, + crate::needless_bool::BOOL_COMPARISON_INFO, + crate::needless_bool::NEEDLESS_BOOL_INFO, + crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, + crate::needless_continue::NEEDLESS_CONTINUE_INFO, + crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, + crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, + crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, + crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO, + crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO, + crate::needless_update::NEEDLESS_UPDATE_INFO, + crate::neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD_INFO, + crate::neg_multiply::NEG_MULTIPLY_INFO, + crate::new_without_default::NEW_WITHOUT_DEFAULT_INFO, + crate::no_effect::NO_EFFECT_INFO, + crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO, + crate::no_effect::UNNECESSARY_OPERATION_INFO, + crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO, + crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO, + crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO, + crate::non_expressive_names::MANY_SINGLE_CHAR_NAMES_INFO, + crate::non_expressive_names::SIMILAR_NAMES_INFO, + crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO, + crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO, + crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, + crate::octal_escapes::OCTAL_ESCAPES_INFO, + crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO, + crate::operators::ABSURD_EXTREME_COMPARISONS_INFO, + crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO, + crate::operators::ASSIGN_OP_PATTERN_INFO, + crate::operators::BAD_BIT_MASK_INFO, + crate::operators::CMP_NAN_INFO, + crate::operators::CMP_OWNED_INFO, + crate::operators::DOUBLE_COMPARISONS_INFO, + crate::operators::DURATION_SUBSEC_INFO, + crate::operators::EQ_OP_INFO, + crate::operators::ERASING_OP_INFO, + crate::operators::FLOAT_ARITHMETIC_INFO, + crate::operators::FLOAT_CMP_INFO, + crate::operators::FLOAT_CMP_CONST_INFO, + crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO, + crate::operators::IDENTITY_OP_INFO, + crate::operators::INEFFECTIVE_BIT_MASK_INFO, + crate::operators::INTEGER_ARITHMETIC_INFO, + crate::operators::INTEGER_DIVISION_INFO, + crate::operators::MISREFACTORED_ASSIGN_OP_INFO, + crate::operators::MODULO_ARITHMETIC_INFO, + crate::operators::MODULO_ONE_INFO, + crate::operators::NEEDLESS_BITWISE_BOOL_INFO, + crate::operators::OP_REF_INFO, + crate::operators::PTR_EQ_INFO, + crate::operators::SELF_ASSIGNMENT_INFO, + crate::operators::VERBOSE_BIT_MASK_INFO, + crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO, + crate::option_if_let_else::OPTION_IF_LET_ELSE_INFO, + crate::overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL_INFO, + crate::panic_in_result_fn::PANIC_IN_RESULT_FN_INFO, + crate::panic_unimplemented::PANIC_INFO, + crate::panic_unimplemented::TODO_INFO, + crate::panic_unimplemented::UNIMPLEMENTED_INFO, + crate::panic_unimplemented::UNREACHABLE_INFO, + crate::partial_pub_fields::PARTIAL_PUB_FIELDS_INFO, + crate::partialeq_ne_impl::PARTIALEQ_NE_IMPL_INFO, + crate::partialeq_to_none::PARTIALEQ_TO_NONE_INFO, + crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO, + crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO, + crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, + crate::precedence::PRECEDENCE_INFO, + crate::ptr::CMP_NULL_INFO, + crate::ptr::INVALID_NULL_PTR_USAGE_INFO, + crate::ptr::MUT_FROM_REF_INFO, + crate::ptr::PTR_ARG_INFO, + crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, + crate::pub_use::PUB_USE_INFO, + crate::question_mark::QUESTION_MARK_INFO, + crate::ranges::MANUAL_RANGE_CONTAINS_INFO, + crate::ranges::RANGE_MINUS_ONE_INFO, + crate::ranges::RANGE_PLUS_ONE_INFO, + crate::ranges::REVERSED_EMPTY_RANGES_INFO, + crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO, + crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO, + crate::redundant_clone::REDUNDANT_CLONE_INFO, + crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO, + crate::redundant_else::REDUNDANT_ELSE_INFO, + crate::redundant_field_names::REDUNDANT_FIELD_NAMES_INFO, + crate::redundant_pub_crate::REDUNDANT_PUB_CRATE_INFO, + crate::redundant_slicing::DEREF_BY_SLICING_INFO, + crate::redundant_slicing::REDUNDANT_SLICING_INFO, + crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO, + crate::ref_option_ref::REF_OPTION_REF_INFO, + crate::reference::DEREF_ADDROF_INFO, + crate::regex::INVALID_REGEX_INFO, + crate::regex::TRIVIAL_REGEX_INFO, + crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO, + crate::returns::LET_AND_RETURN_INFO, + crate::returns::NEEDLESS_RETURN_INFO, + crate::same_name_method::SAME_NAME_METHOD_INFO, + crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO, + crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO, + crate::serde_api::SERDE_API_MISUSE_INFO, + crate::shadow::SHADOW_REUSE_INFO, + crate::shadow::SHADOW_SAME_INFO, + crate::shadow::SHADOW_UNRELATED_INFO, + crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO, + crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO, + crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO, + crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO, + crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO, + crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO, + crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO, + crate::strings::STRING_ADD_INFO, + crate::strings::STRING_ADD_ASSIGN_INFO, + crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, + crate::strings::STRING_LIT_AS_BYTES_INFO, + crate::strings::STRING_SLICE_INFO, + crate::strings::STRING_TO_STRING_INFO, + crate::strings::STR_TO_STRING_INFO, + crate::strings::TRIM_SPLIT_WHITESPACE_INFO, + crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO, + crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO, + crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO, + crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO, + crate::suspicious_xor_used_as_pow::SUSPICIOUS_XOR_USED_AS_POW_INFO, + crate::swap::ALMOST_SWAPPED_INFO, + crate::swap::MANUAL_SWAP_INFO, + crate::swap_ptr_to_ref::SWAP_PTR_TO_REF_INFO, + crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO, + crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO, + crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, + crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO, + crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO, + crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO, + crate::transmute::CROSSPOINTER_TRANSMUTE_INFO, + crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, + crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, + crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO, + crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, + crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, + crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, + crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, + crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, + crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, + crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO, + crate::transmute::TRANSMUTING_NULL_INFO, + crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO, + crate::transmute::USELESS_TRANSMUTE_INFO, + crate::transmute::WRONG_TRANSMUTE_INFO, + crate::types::BORROWED_BOX_INFO, + crate::types::BOX_COLLECTION_INFO, + crate::types::LINKEDLIST_INFO, + crate::types::OPTION_OPTION_INFO, + crate::types::RC_BUFFER_INFO, + crate::types::RC_MUTEX_INFO, + crate::types::REDUNDANT_ALLOCATION_INFO, + crate::types::TYPE_COMPLEXITY_INFO, + crate::types::VEC_BOX_INFO, + crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO, + crate::unicode::INVISIBLE_CHARACTERS_INFO, + crate::unicode::NON_ASCII_LITERAL_INFO, + crate::unicode::UNICODE_NOT_NFC_INFO, + crate::uninit_vec::UNINIT_VEC_INFO, + crate::unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD_INFO, + crate::unit_types::LET_UNIT_VALUE_INFO, + crate::unit_types::UNIT_ARG_INFO, + crate::unit_types::UNIT_CMP_INFO, + crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO, + crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO, + crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, + crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, + crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, + crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO, + crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO, + crate::unused_async::UNUSED_ASYNC_INFO, + crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO, + crate::unused_peekable::UNUSED_PEEKABLE_INFO, + crate::unused_rounding::UNUSED_ROUNDING_INFO, + crate::unused_self::UNUSED_SELF_INFO, + crate::unused_unit::UNUSED_UNIT_INFO, + crate::unwrap::PANICKING_UNWRAP_INFO, + crate::unwrap::UNNECESSARY_UNWRAP_INFO, + crate::unwrap_in_result::UNWRAP_IN_RESULT_INFO, + crate::upper_case_acronyms::UPPER_CASE_ACRONYMS_INFO, + crate::use_self::USE_SELF_INFO, + crate::useless_conversion::USELESS_CONVERSION_INFO, + crate::vec::USELESS_VEC_INFO, + crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO, + crate::wildcard_imports::ENUM_GLOB_USE_INFO, + crate::wildcard_imports::WILDCARD_IMPORTS_INFO, + crate::write::PRINTLN_EMPTY_STRING_INFO, + crate::write::PRINT_LITERAL_INFO, + crate::write::PRINT_STDERR_INFO, + crate::write::PRINT_STDOUT_INFO, + crate::write::PRINT_WITH_NEWLINE_INFO, + crate::write::USE_DEBUG_INFO, + crate::write::WRITELN_EMPTY_STRING_INFO, + crate::write::WRITE_LITERAL_INFO, + crate::write::WRITE_WITH_NEWLINE_INFO, + crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, + crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, +]; diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 218dbeaddcad..9da64ffc13e1 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -9,6 +9,7 @@ use clippy_utils::{ }; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ @@ -274,9 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } let typeck = cx.typeck_results(); - let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) { - x - } else { + let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { report(cx, expr, state, data); @@ -806,30 +805,39 @@ fn walk_parents<'tcx>( .position(|arg| arg.hir_id == child_id) .zip(expr_sig(cx, func)) .and_then(|(i, sig)| { - sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty { - // Type inference for closures can depend on how they're called. Only go by the explicit - // types here. - Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), - None => { - if let ty::Param(param_ty) = ty.skip_binder().kind() { - needless_borrow_impl_arg_position( - cx, - possible_borrowers, - parent, - i, - *param_ty, - e, - precedence, - msrv, - ) - } else { - ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) - .position_for_arg() - } - }, + sig.input_with_hir(i).map(|(hir_ty, ty)| { + match hir_ty { + // Type inference for closures can depend on how they're called. Only go by the explicit + // types here. + Some(hir_ty) => { + binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()) + }, + None => { + // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739 + // `!call_is_qualified(func)` for https://github.com/rust-lang/rust-clippy/issues/9782 + if e.hir_id == child_id + && !call_is_qualified(func) + && let ty::Param(param_ty) = ty.skip_binder().kind() + { + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i, + *param_ty, + e, + precedence, + msrv, + ) + } else { + ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) + .position_for_arg() + } + }, + } }) }), - ExprKind::MethodCall(_, receiver, args, _) => { + ExprKind::MethodCall(method, receiver, args, _) => { let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(); if receiver.hir_id == child_id { // Check for calls to trait methods where the trait is implemented on a reference. @@ -867,7 +875,9 @@ fn walk_parents<'tcx>( } args.iter().position(|arg| arg.hir_id == child_id).map(|i| { let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1]; - if let ty::Param(param_ty) = ty.kind() { + // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739 + // `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782 + if e.hir_id == child_id && method.args.is_none() && let ty::Param(param_ty) = ty.kind() { needless_borrow_impl_arg_position( cx, possible_borrowers, @@ -1045,13 +1055,25 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { v.0 } +fn call_is_qualified(expr: &Expr<'_>) -> bool { + if let ExprKind::Path(path) = &expr.kind { + match path { + QPath::Resolved(_, path) => path.segments.last().map_or(false, |segment| segment.args.is_some()), + QPath::TypeRelative(_, segment) => segment.args.is_some(), + QPath::LangItem(..) => false, + } + } else { + false + } +} + // Checks whether: // * child is an expression of the form `&e` in an argument position requiring an `impl Trait` // * `e`'s type implements `Trait` and is copyable // If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. // The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to // be moved, but it cannot be. -#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments, clippy::too_many_lines)] fn needless_borrow_impl_arg_position<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, @@ -1113,6 +1135,16 @@ fn needless_borrow_impl_arg_position<'tcx>( return Position::Other(precedence); } + // See: + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 + if projection_predicates + .iter() + .any(|projection_predicate| is_mixed_projection_predicate(cx, callee_def_id, projection_predicate)) + { + return Position::Other(precedence); + } + // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same // elements are modified each time `check_referent` is called. let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); @@ -1192,6 +1224,37 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { }) } +fn is_mixed_projection_predicate<'tcx>( + cx: &LateContext<'tcx>, + callee_def_id: DefId, + projection_predicate: &ProjectionPredicate<'tcx>, +) -> bool { + let generics = cx.tcx.generics_of(callee_def_id); + // The predicate requires the projected type to equal a type parameter from the parent context. + if let Some(term_ty) = projection_predicate.term.ty() + && let ty::Param(term_param_ty) = term_ty.kind() + && (term_param_ty.index as usize) < generics.parent_count + { + // The inner-most self type is a type parameter from the current function. + let mut projection_ty = projection_predicate.projection_ty; + loop { + match projection_ty.self_ty().kind() { + ty::Projection(inner_projection_ty) => { + projection_ty = *inner_projection_ty; + } + ty::Param(param_ty) => { + return (param_ty.index as usize) >= generics.parent_count; + } + _ => { + return false; + } + } + } + } else { + false + } +} + fn referent_used_exactly_once<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, @@ -1203,6 +1266,8 @@ fn referent_used_exactly_once<'tcx>( && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind && !place.has_deref() + // Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710) + && TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none() { let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); if possible_borrowers @@ -1320,6 +1385,7 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc continue; }, ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), + ty::Projection(_) if ty.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty), ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => { Position::ReborrowStable(precedence).into() }, @@ -1346,11 +1412,9 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc | ty::Closure(..) | ty::Never | ty::Tuple(_) - | ty::Projection(_) => Position::DerefStable( - precedence, - ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds()), - ) - .into(), + | ty::Projection(_) => { + Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds())).into() + }, }; } } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 5ab7144e2909..68122b4cef57 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::macro_backtrace; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; use rustc_lint::{LateContext, LateLintPass}; @@ -89,7 +88,7 @@ impl DisallowedMacros { &format!("use of a disallowed macro `{}`", conf.path()), |diag| { if let Some(reason) = conf.reason() { - diag.note(&format!("{reason} (from clippy.toml)")); + diag.note(reason); } }, ); @@ -104,7 +103,7 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.disallowed.insert(id, index); } } diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 6ac85606d9c7..ca8671c8f1aa 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -79,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.disallowed.insert(id, index); } } @@ -104,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { if let Some(reason) = conf.reason() { - diag.note(&format!("{reason} (from clippy.toml)")); + diag.note(reason); } }); } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index c7131fc164d3..aee3d8c4f085 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::{Namespace, Res}; +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}; @@ -53,8 +53,8 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedTypes { conf_disallowed: Vec, - def_ids: FxHashMap>, - prim_tys: FxHashMap>, + def_ids: FxHashMap, + prim_tys: FxHashMap, } impl DisallowedTypes { @@ -69,13 +69,13 @@ impl DisallowedTypes { fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { match res { Res::Def(_, did) => { - if let Some(reason) = self.def_ids.get(did) { - emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref()); + if let Some(&index) = self.def_ids.get(did) { + emit(cx, &cx.tcx.def_path_str(*did), span, &self.conf_disallowed[index]); } }, Res::PrimTy(prim) => { - if let Some(reason) = self.prim_tys.get(prim) { - emit(cx, prim.name_str(), span, reason.as_deref()); + if let Some(&index) = self.prim_tys.get(prim) { + emit(cx, prim.name_str(), span, &self.conf_disallowed[index]); } }, _ => {}, @@ -87,17 +87,19 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_crate(&mut self, cx: &LateContext<'_>) { - for conf in &self.conf_disallowed { + for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)")); - match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { - Res::Def(_, id) => { - self.def_ids.insert(id, reason); - }, - Res::PrimTy(ty) => { - self.prim_tys.insert(ty, reason); - }, - _ => {}, + + for res in clippy_utils::def_path_res(cx, &segs) { + match res { + Res::Def(_, id) => { + self.def_ids.insert(id, index); + }, + Res::PrimTy(ty) => { + self.prim_tys.insert(ty, index); + }, + _ => {}, + } } } } @@ -119,14 +121,14 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { } } -fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) { +fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &conf::DisallowedPath) { span_lint_and_then( cx, DISALLOWED_TYPES, span, &format!("`{name}` is not allowed according to config"), |diag| { - if let Some(reason) = reason { + if let Some(reason) = conf.reason() { diag.note(reason); } }, diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index daaab79fef9a..4557e4328854 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -11,7 +11,7 @@ use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Applicability, Handler, MultiSpan, SuggestionStyle}; +use rustc_errors::{Applicability, Handler, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Expr}; @@ -221,6 +221,42 @@ declare_clippy_lint! { "possible typo for an intra-doc link" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the doc comments of publicly visible + /// safe functions and traits and warns if there is a `# Safety` section. + /// + /// ### Why is this bad? + /// Safe functions and traits are safe to implement and therefore do not + /// need to describe safety preconditions that users are required to uphold. + /// + /// ### Examples + /// ```rust + ///# type Universe = (); + /// /// # Safety + /// /// + /// /// This function should not be called before the horsemen are ready. + /// pub fn start_apocalypse_but_safely(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + /// + /// The function is safe, so there shouldn't be any preconditions + /// that have to be explained for safety reasons. + /// + /// ```rust + ///# type Universe = (); + /// /// This function should really be documented + /// pub fn start_apocalypse(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub UNNECESSARY_SAFETY_DOC, + style, + "`pub fn` or `pub trait` with `# Safety` docs" +} + #[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { @@ -243,7 +279,8 @@ impl_lint_pass!(DocMarkdown => [ MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, - NEEDLESS_DOCTEST_MAIN + NEEDLESS_DOCTEST_MAIN, + UNNECESSARY_SAFETY_DOC, ]); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { @@ -254,7 +291,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; match item.kind { 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)) { @@ -265,29 +302,26 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers( - cx, - item.owner_id.def_id, - item.span, - sig, - headers, - Some(body_id), - fpu.panic_span, - ); + lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span); } }, hir::ItemKind::Impl(impl_) => { self.in_trait_impl = impl_.of_trait.is_some(); }, - hir::ItemKind::Trait(_, unsafety, ..) => { - if !headers.safety && unsafety == hir::Unsafety::Unsafe { - span_lint( - cx, - MISSING_SAFETY_DOC, - item.span, - "docs for unsafe trait missing `# Safety` section", - ); - } + hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) { + (false, hir::Unsafety::Unsafe) => span_lint( + cx, + MISSING_SAFETY_DOC, + cx.tcx.def_span(item.owner_id), + "docs for unsafe trait missing `# Safety` section", + ), + (true, hir::Unsafety::Normal) => span_lint( + cx, + UNNECESSARY_SAFETY_DOC, + cx.tcx.def_span(item.owner_id), + "docs for safe trait have unnecessary `# Safety` section", + ), + _ => (), }, _ => (), } @@ -301,17 +335,17 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; 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.def_id, item.span, sig, headers, None, None); + lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, None, None); } } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) { return; } @@ -323,23 +357,14 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers( - cx, - item.owner_id.def_id, - item.span, - sig, - headers, - Some(body_id), - fpu.panic_span, - ); + lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span); } } } -fn lint_for_missing_headers<'tcx>( - cx: &LateContext<'tcx>, +fn lint_for_missing_headers( + cx: &LateContext<'_>, def_id: LocalDefId, - span: impl Into + Copy, sig: &hir::FnSig<'_>, headers: DocHeaders, body_id: Option, @@ -359,13 +384,21 @@ fn lint_for_missing_headers<'tcx>( return; } - if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe { - span_lint( + let span = cx.tcx.def_span(def_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( @@ -467,7 +500,7 @@ struct DocHeaders { panics: bool, } -fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &'a [Attribute]) -> DocHeaders { +fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[Attribute]) -> Option { use pulldown_cmark::{BrokenLink, CowStr, Options}; /// 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 are @@ -488,11 +521,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs } else if attr.has_name(sym::doc) { // ignore mix of sugared and non-sugared doc // don't trigger the safety or errors check - return DocHeaders { - safety: true, - errors: true, - panics: true, - }; + return None; } } @@ -504,7 +533,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs } if doc.is_empty() { - return DocHeaders::default(); + return Some(DocHeaders::default()); } let mut cb = fake_broken_link_callback; @@ -527,7 +556,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs (previous, current) => Err(((previous, previous_range), (current, current_range))), } }); - check_doc(cx, valid_idents, events, &spans) + Some(check_doc(cx, valid_idents, events, &spans)) } const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 223545fa7984..b77b5621b4c6 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -250,7 +250,7 @@ impl LateLintPass<'_> for EnumVariantNames { let item_name = item.ident.name.as_str(); let item_camel = to_camel_case(item_name); if !item.span.from_expansion() && is_present_in_source(cx, item.span) { - if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() { + if let Some((mod_name, mod_camel)) = self.modules.last() { // constants don't have surrounding modules if !mod_camel.is_empty() { if mod_name == &item.ident.name { diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index b40cb7cddaf1..c691e6c5402d 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -67,16 +66,14 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Let(let_expr) = expr.kind; - if unary_pattern(let_expr.pat); + if !in_external_macro(cx.sess(), expr.span) + && let ExprKind::Let(let_expr) = expr.kind + && unary_pattern(let_expr.pat) { let exp_ty = cx.typeck_results().expr_ty(let_expr.init); let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); - if is_structural_partial_eq(cx, exp_ty, pat_ty); - then { + let mut applicability = Applicability::MachineApplicable; - let mut applicability = Applicability::MachineApplicable; + if is_structural_partial_eq(cx, exp_ty, pat_ty) { let pat_str = match let_expr.pat.kind { PatKind::Struct(..) => format!( "({})", @@ -96,6 +93,20 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { ), applicability, ); + } else { + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using `matches!`", + "try", + format!( + "matches!({}, {})", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + applicability, + ); } } } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 7f1a4c4beb1f..1d09adec12f3 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -176,13 +176,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { } } - fn fake_read( - &mut self, - _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 453471c8cdda..fc2912f696e0 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,8 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, Span}; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; declare_clippy_lint! { /// ### What it does @@ -83,6 +86,12 @@ pub struct ExcessiveBools { max_fn_params_bools: u64, } +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +enum Kind { + Struct, + Fn, +} + impl ExcessiveBools { #[must_use] pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self { @@ -92,21 +101,20 @@ impl ExcessiveBools { } } - fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) { - match fn_sig.header.ext { - Extern::Implicit(_) | Extern::Explicit(_, _) => return, - Extern::None => (), + fn too_many_bools<'tcx>(&self, tys: impl Iterator>, kind: Kind) -> bool { + if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() { + (if Kind::Fn == kind { + self.max_fn_params_bools + } else { + self.max_struct_bools + }) < bools + } else { + false } + } - let fn_sig_bools = fn_sig - .decl - .inputs - .iter() - .filter(|param| is_bool_ty(¶m.ty)) - .count() - .try_into() - .unwrap(); - if self.max_fn_params_bools < fn_sig_bools { + fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) { + if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { span_lint_and_help( cx, FN_PARAMS_EXCESSIVE_BOOLS, @@ -121,56 +129,55 @@ impl ExcessiveBools { impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); -fn is_bool_ty(ty: &Ty) -> bool { - if let TyKind::Path(None, path) = &ty.kind { - if let [name] = path.segments.as_slice() { - return name.ident.name == sym::bool; +impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if item.span.from_expansion() { + return; + } + if let ItemKind::Struct(variant_data, _) = &item.kind { + if has_repr_attr(cx, item.hir_id()) { + return; + } + + if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) { + span_lint_and_help( + cx, + STRUCT_EXCESSIVE_BOOLS, + item.span, + &format!("more than {} bools in a struct", self.max_struct_bools), + None, + "consider using a state machine or refactoring bools into two-variant enums", + ); + } } } - false -} -impl EarlyLintPass for ExcessiveBools { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if item.span.from_expansion() { - return; + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) { + // functions with a body are already checked by `check_fn` + if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind + && fn_sig.header.abi == Abi::Rust + { + self.check_fn_sig(cx, fn_sig.decl, fn_sig.span); } - match &item.kind { - ItemKind::Struct(variant_data, _) => { - if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) { - return; - } + } - let struct_bools = variant_data - .fields() - .iter() - .filter(|field| is_bool_ty(&field.ty)) - .count() - .try_into() - .unwrap(); - if self.max_struct_bools < struct_bools { - span_lint_and_help( - cx, - STRUCT_EXCESSIVE_BOOLS, - item.span, - &format!("more than {} bools in a struct", self.max_struct_bools), - None, - "consider using a state machine or refactoring bools into two-variant enums", - ); - } - }, - ItemKind::Impl(box Impl { - of_trait: None, items, .. - }) - | ItemKind::Trait(box Trait { items, .. }) => { - for item in items { - if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { - self.check_fn_sig(cx, sig, item.span); - } - } - }, - ItemKind::Fn(box Fn { sig, .. }) => self.check_fn_sig(cx, sig, item.span), - _ => (), + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + _: &'tcx Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let Some(fn_header) = fn_kind.header() + && fn_header.abi == Abi::Rust + && get_parent_as_impl(cx.tcx, hir_id) + .map_or(true, + |impl_item| impl_item.of_trait.is_none() + ) + { + self.check_fn_sig(cx, fn_decl, span); } } } diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 0a633f242a5f..9a1058470e18 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { } } -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { +fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Expr, ImplItemKind}; diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs new file mode 100644 index 000000000000..00f5ba56496e --- /dev/null +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_c_void; +use clippy_utils::{match_def_path, path_def_id, paths}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::RawPtr; +use rustc_middle::ty::TypeAndMut; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks if we're passing a `c_void` raw pointer to `{Box,Rc,Arc,Weak}::from_raw(_)` + /// + /// ### Why is this bad? + /// When dealing with `c_void` raw pointers in FFI, it is easy to run into the pitfall of calling `from_raw` with the `c_void` pointer. + /// The type signature of `Box::from_raw` is `fn from_raw(raw: *mut T) -> Box`, so if you pass a `*mut c_void` you will get a `Box` (and similarly for `Rc`, `Arc` and `Weak`). + /// For this to be safe, `c_void` would need to have the same memory layout as the original type, which is often not the case. + /// + /// ### Example + /// ```rust + /// # use std::ffi::c_void; + /// let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void; + /// let _ = unsafe { Box::from_raw(ptr) }; + /// ``` + /// Use instead: + /// ```rust + /// # use std::ffi::c_void; + /// # let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void; + /// let _ = unsafe { Box::from_raw(ptr as *mut usize) }; + /// ``` + /// + #[clippy::version = "1.66.0"] + pub FROM_RAW_WITH_VOID_PTR, + suspicious, + "creating a `Box` from a void raw pointer" +} +declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]); + +impl LateLintPass<'_> for FromRawWithVoidPtr { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Call(box_from_raw, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind + && seg.ident.name == sym!(from_raw) + && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) + && let arg_kind = cx.typeck_results().expr_ty(arg).kind() + && let RawPtr(TypeAndMut { ty, .. }) = arg_kind + && is_c_void(cx, *ty) { + let msg = format!("creating a `{type_str}` from a void raw pointer"); + span_lint_and_help(cx, FROM_RAW_WITH_VOID_PTR, expr.span, &msg, Some(arg.span), "cast this to a pointer of the appropriate type"); + } + } +} + +/// Checks whether a `DefId` matches `Box`, `Rc`, `Arc`, or one of the `Weak` types. +/// Returns a static string slice with the name of the type, if one was found. +fn def_id_matches_type(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { + // Box + if Some(def_id) == cx.tcx.lang_items().owned_box() { + return Some("Box"); + } + + if let Some(symbol) = cx.tcx.get_diagnostic_name(def_id) { + if symbol == sym::Arc { + return Some("Arc"); + } else if symbol == sym::Rc { + return Some("Rc"); + } + } + + if match_def_path(cx, def_id, &paths::WEAK_RC) || match_def_path(cx, def_id, &paths::WEAK_ARC) { + Some("Weak") + } else { + None + } +} diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 90911e0bf259..ae0e08334463 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -254,7 +254,7 @@ declare_clippy_lint! { /// Ok(()) /// } /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub RESULT_LARGE_ERR, perf, "function returning `Result` with large `Err` type" diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index bff69f915184..d22bede36b41 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -50,7 +50,9 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + } else if is_public + && !is_proc_macro(cx.sess(), attrs) + && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { check_must_use_candidate( cx, @@ -175,7 +177,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) return false; // ignore `_` patterns } if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { - is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), pat.span, tys) + is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), tys) } else { false } @@ -183,7 +185,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc]; -fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool { +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool { match *ty.kind() { // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, @@ -192,12 +194,12 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m || KNOWN_WRAPPER_TYS .iter() .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did())) - && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) + && substs.types().any(|ty| is_mutable_ty(cx, ty, tys)) }, - ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)), - ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), + ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, tys)), + ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, tys), ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { - mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) + mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys) }, // calling something constitutes a side effect, so return true on all callables // also never calls need not be used, so return true for them, too @@ -225,12 +227,7 @@ fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bo let mut tys = DefIdSet::default(); for arg in args { if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - cx, - cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), - arg.span, - &mut tys, - ) + && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys) && is_mutated_static(arg) { return ControlFlow::Break(()); @@ -243,12 +240,7 @@ fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bo let mut tys = DefIdSet::default(); for arg in std::iter::once(receiver).chain(args.iter()) { if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - cx, - cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), - arg.span, - &mut tys, - ) + && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys) && is_mutated_static(arg) { return ControlFlow::Break(()); diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 5c63fb2acb11..f7e30b051a69 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -2,12 +2,12 @@ use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Adt, Ty}; use rustc_span::{sym, Span}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::trait_ref_of_method; -use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item}; +use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; @@ -84,17 +84,57 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S } fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { - let ty_size = approx_ty_size(cx, err_ty); - if ty_size >= large_err_threshold { - span_lint_and_then( - cx, - RESULT_LARGE_ERR, - hir_ty_span, - "the `Err`-variant returned from this function is very large", - |diag: &mut Diagnostic| { - diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); - diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); - }, - ); + if_chain! { + if let Adt(adt, subst) = err_ty.kind(); + if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local(); + if let Some(hir::Node::Item(item)) = cx + .tcx + .hir() + .find_by_def_id(local_def_id); + if let hir::ItemKind::Enum(ref def, _) = item.kind; + then { + let variants_size = AdtVariantInfo::new(cx, *adt, subst); + if variants_size[0].size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag| { + diag.span_label( + def.variants[variants_size[0].ind].span, + format!("the largest variant contains at least {} bytes", variants_size[0].size), + ); + + for variant in &variants_size[1..] { + if variant.size >= large_err_threshold { + let variant_def = &def.variants[variant.ind]; + diag.span_label( + variant_def.span, + format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size), + ); + } + } + + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + } + ); + } + } + else { + let ty_size = approx_ty_size(cx, err_ty); + if ty_size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag: &mut Diagnostic| { + diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + }, + ); + } + } } } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 94e06cf704ba..64a4a3fa741b 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -66,8 +66,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { use rustc_span::BytePos; - fn suggestion<'tcx>( - cx: &LateContext<'tcx>, + fn suggestion( + cx: &LateContext<'_>, diag: &mut Diagnostic, generics_span: Span, generics_suggestion_span: Span, diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index c7b5badaae51..0d5099bde6de 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -207,8 +207,8 @@ impl SliceLintInformation { } } -fn filter_lintable_slices<'a, 'tcx>( - cx: &'a LateContext<'tcx>, +fn filter_lintable_slices<'tcx>( + cx: &LateContext<'tcx>, slice_lint_info: FxIndexMap, max_suggested_slice: u64, scope: &'tcx hir::Expr<'tcx>, diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index af40a5a8187e..4cd7dff4cfd7 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -171,11 +171,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { /// Returns a tuple of options with the start and end (exclusive) values of /// the range. If the start or end is not constant, None is returned. -fn to_const_range<'tcx>( - cx: &LateContext<'tcx>, - range: higher::Range<'_>, - array_size: u128, -) -> (Option, Option) { +fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option, Option) { let s = range .start .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c)); diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs new file mode 100644 index 000000000000..60754b224fc8 --- /dev/null +++ b/clippy_lints/src/instant_subtraction.rs @@ -0,0 +1,184 @@ +use clippy_utils::{ + diagnostics::{self, span_lint_and_sugg}, + meets_msrv, msrvs, source, + sugg::Sugg, + ty, +}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{source_map::Spanned, sym}; + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between `Instant::now()` and another `Instant`. + /// + /// ### Why is this bad? + /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns + /// as `Instant` subtraction saturates. + /// + /// `prev_instant.elapsed()` also more clearly signals intention. + /// + /// ### Example + /// ```rust + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = Instant::now() - prev_instant; + /// ``` + /// Use instead: + /// ```rust + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = prev_instant.elapsed(); + /// ``` + #[clippy::version = "1.65.0"] + pub MANUAL_INSTANT_ELAPSED, + pedantic, + "subtraction between `Instant::now()` and previous `Instant`" +} + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between an [`Instant`] and a [`Duration`]. + /// + /// ### Why is this bad? + /// Unchecked subtraction could cause underflow on certain platforms, leading to + /// unintentional panics. + /// + /// ### Example + /// ```rust + /// # use std::time::{Instant, Duration}; + /// let time_passed = Instant::now() - Duration::from_secs(5); + /// ``` + /// + /// Use instead: + /// ```rust + /// # use std::time::{Instant, Duration}; + /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); + /// ``` + /// + /// [`Duration`]: std::time::Duration + /// [`Instant::now()`]: std::time::Instant::now; + #[clippy::version = "1.65.0"] + pub UNCHECKED_DURATION_SUBTRACTION, + suspicious, + "finds unchecked subtraction of a 'Duration' from an 'Instant'" +} + +pub struct InstantSubtraction { + msrv: Option, +} + +impl InstantSubtraction { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]); + +impl LateLintPass<'_> for InstantSubtraction { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = expr.kind + { + if_chain! { + if is_instant_now_call(cx, lhs); + + if is_an_instant(cx, rhs); + if let Some(sugg) = Sugg::hir_opt(cx, rhs); + + then { + print_manual_instant_elapsed_sugg(cx, expr, sugg) + } else { + if_chain! { + if !expr.span.from_expansion(); + if meets_msrv(self.msrv, msrvs::TRY_FROM); + + if is_an_instant(cx, lhs); + if is_a_duration(cx, rhs); + + then { + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr) + } + } + } + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { + if let ExprKind::Call(fn_expr, []) = expr_block.kind + && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) + && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) + { + true + } else { + false + } +} + +fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + + match expr_ty.kind() { + rustc_middle::ty::Adt(def, _) => clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT), + _ => false, + } +} + +fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration) +} + +fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { + span_lint_and_sugg( + cx, + MANUAL_INSTANT_ELAPSED, + expr.span, + "manual implementation of `Instant::elapsed`", + "try", + format!("{}.elapsed()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); +} + +fn print_unchecked_duration_subtraction_sugg( + cx: &LateContext<'_>, + left_expr: &Expr<'_>, + right_expr: &Expr<'_>, + expr: &Expr<'_>, +) { + let mut applicability = Applicability::MachineApplicable; + + let left_expr = + source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability); + let right_expr = source::snippet_with_applicability( + cx, + right_expr.span, + "std::time::Duration::from_secs(1)", + &mut applicability, + ); + + diagnostics::span_lint_and_sugg( + cx, + UNCHECKED_DURATION_SUBTRACTION, + expr.span, + "unchecked subtraction of a 'Duration' from an 'Instant'", + "try", + format!("{left_expr}.checked_sub({right_expr}).unwrap()"), + applicability, + ); +} diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index f793abdfda34..1b14e525d9a8 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -63,58 +63,54 @@ impl IntPlusOne { fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option { match (binop, &lhs.kind, &rhs.kind) { // case where `x - 1 >= ...` or `-1 + x >= ...` - (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => { + (BinOpKind::Ge, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) => { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { // `-1 + x` - (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, // `x - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } }, // case where `... >= y + 1` or `... >= 1 + y` - (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) - if rhskind.node == BinOpKind::Add => - { + (BinOpKind::Ge, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) if rhskind.node == BinOpKind::Add => { match (&rhslhs.kind, &rhsrhs.kind) { // `y + 1` and `1 + y` - (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { + (ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, - (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { + (_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, } }, // case where `x + 1 <= ...` or `1 + x <= ...` - (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) - if lhskind.node == BinOpKind::Add => - { + (BinOpKind::Le, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) if lhskind.node == BinOpKind::Add => { match (&lhslhs.kind, &lhsrhs.kind) { // `1 + x` and `x + 1` - (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { + (ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, - (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { + (_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } }, // case where `... >= y - 1` or `... >= -1 + y` - (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => { + (BinOpKind::Le, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) => { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { // `-1 + y` - (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, // `y - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 0ef77e03de90..6ea637412d5b 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); -fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { +fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(FullInt, FullInt)> { if let ExprKind::Cast(cast_exp, _) = expr.kind { let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp); let cast_ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 06e957285499..b18456ee5234 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,12 +1,15 @@ //! lint when there is a large size difference between variants on an enum use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy}; +use clippy_utils::{ + diagnostics::span_lint_and_then, + ty::{approx_ty_size, is_copy, AdtVariantInfo}, +}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty}; +use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -72,49 +75,6 @@ impl LargeEnumVariant { } } -struct FieldInfo { - ind: usize, - size: u64, -} - -struct VariantInfo { - ind: usize, - size: u64, - fields_size: Vec, -} - -fn variants_size<'tcx>( - cx: &LateContext<'tcx>, - adt: AdtDef<'tcx>, - subst: &'tcx List>, -) -> Vec { - let mut variants_size = adt - .variants() - .iter() - .enumerate() - .map(|(i, variant)| { - let mut fields_size = variant - .fields - .iter() - .enumerate() - .map(|(i, f)| FieldInfo { - ind: i, - size: approx_ty_size(cx, f.ty(cx.tcx, subst)), - }) - .collect::>(); - fields_size.sort_by(|a, b| (a.size.cmp(&b.size))); - - VariantInfo { - ind: i, - size: fields_size.iter().map(|info| info.size).sum(), - fields_size, - } - }) - .collect::>(); - variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); - variants_size -} - impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { @@ -130,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { if adt.variants().len() <= 1 { return; } - let variants_size = variants_size(cx, *adt, subst); + let variants_size = AdtVariantInfo::new(cx, *adt, subst); let mut difference = variants_size[0].size - variants_size[1].size; if difference > self.maximum_size_difference_allowed { @@ -173,16 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { .fields_size .iter() .rev() - .map_while(|val| { + .map_while(|&(ind, size)| { if difference > self.maximum_size_difference_allowed { - difference = difference.saturating_sub(val.size); + difference = difference.saturating_sub(size); Some(( - fields[val.ind].ty.span, + fields[ind].ty.span, format!( "Box<{}>", snippet_with_applicability( cx, - fields[val.ind].ty.span, + fields[ind].ty.span, "..", &mut applicability ) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index b0cba40c27a5..4c133c06a157 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -366,8 +366,7 @@ fn check_for_is_empty<'tcx>( } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { - if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) - { + if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method if let Some(name) = get_item_name(cx, method) { if name.as_str() == "is_empty" { diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index b7798b1c1d74..61f87b91400d 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,13 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type}; +use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; use clippy_utils::{is_must_use_func_call, paths}; -use if_chain::if_chain; use rustc_hir::{Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, Symbol}; declare_clippy_lint! { /// ### What it does @@ -30,13 +28,14 @@ declare_clippy_lint! { #[clippy::version = "1.42.0"] pub LET_UNDERSCORE_MUST_USE, restriction, - "non-binding let on a `#[must_use]` expression" + "non-binding `let` on a `#[must_use]` expression" } declare_clippy_lint! { /// ### What it does - /// Checks for `let _ = sync_lock`. - /// This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`. + /// Checks for `let _ = sync_lock`. This supports `mutex` and `rwlock` in + /// `parking_lot`. For `std` locks see the `rustc` lint + /// [`let_underscore_lock`](https://doc.rust-lang.org/nightly/rustc/lints/listing/deny-by-default.html#let-underscore-lock) /// /// ### Why is this bad? /// This statement immediately drops the lock instead of @@ -57,50 +56,41 @@ declare_clippy_lint! { #[clippy::version = "1.43.0"] pub LET_UNDERSCORE_LOCK, correctness, - "non-binding let on a synchronization lock" + "non-binding `let` on a synchronization lock" } declare_clippy_lint! { /// ### What it does - /// Checks for `let _ = ` - /// where expr has a type that implements `Drop` + /// Checks for `let _ = ` where the resulting type of expr implements `Future` /// /// ### Why is this bad? - /// This statement immediately drops the initializer - /// expression instead of extending its lifetime to the end of the scope, which - /// is often not intended. To extend the expression's lifetime to the end of the - /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to - /// explicitly drop the expression, `std::mem::drop` conveys your intention - /// better and is less error-prone. + /// Futures must be polled for work to be done. The original intention was most likely to await the future + /// and ignore the resulting value. /// /// ### Example /// ```rust - /// # struct DroppableItem; - /// { - /// let _ = DroppableItem; - /// // ^ dropped here - /// /* more code */ + /// async fn foo() -> Result<(), ()> { + /// Ok(()) /// } + /// let _ = foo(); /// ``` /// /// Use instead: /// ```rust - /// # struct DroppableItem; - /// { - /// let _droppable = DroppableItem; - /// /* more code */ - /// // dropped at end of scope + /// # async fn context() { + /// async fn foo() -> Result<(), ()> { + /// Ok(()) /// } + /// let _ = foo().await; + /// # } /// ``` - #[clippy::version = "1.50.0"] - pub LET_UNDERSCORE_DROP, - pedantic, - "non-binding let on a type that implements `Drop`" + #[clippy::version = "1.66"] + pub LET_UNDERSCORE_FUTURE, + suspicious, + "non-binding `let` on a future" } -declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); - -const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard]; +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE]); const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::PARKING_LOT_MUTEX_GUARD, @@ -110,64 +100,53 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { - if in_external_macro(cx.tcx.sess, local.span) { - return; - } - - if_chain! { - if let PatKind::Wild = local.pat.kind; - if let Some(init) = local.init; - then { - let init_ty = cx.typeck_results().expr_ty(init); - let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => { - SYNC_GUARD_SYMS - .iter() - .any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym)) - || SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) - }, - - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }); - if contains_sync_guard { - span_lint_and_help( - cx, - LET_UNDERSCORE_LOCK, - local.span, - "non-binding let on a synchronization lock", - None, - "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`", - ); - } else if init_ty.needs_drop(cx.tcx, cx.param_env) { - span_lint_and_help( - cx, - LET_UNDERSCORE_DROP, - local.span, - "non-binding `let` on a type that implements `Drop`", - None, - "consider using an underscore-prefixed named \ + if !in_external_macro(cx.tcx.sess, local.span) + && let PatKind::Wild = local.pat.kind + && let Some(init) = local.init + { + let init_ty = cx.typeck_results().expr_ty(init); + let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }); + if contains_sync_guard { + span_lint_and_help( + cx, + LET_UNDERSCORE_LOCK, + local.span, + "non-binding `let` on a synchronization lock", + None, + "consider using an underscore-prefixed named \ binding or dropping explicitly with `std::mem::drop`", - ); - } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { - span_lint_and_help( - cx, - LET_UNDERSCORE_MUST_USE, - local.span, - "non-binding let on an expression with `#[must_use]` type", - None, - "consider explicitly using expression value", - ); - } else if is_must_use_func_call(cx, init) { - span_lint_and_help( - cx, - LET_UNDERSCORE_MUST_USE, - local.span, - "non-binding let on a result of a `#[must_use]` function", - None, - "consider explicitly using function result", - ); - } + ); + } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() + && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) { + span_lint_and_help( + cx, + LET_UNDERSCORE_FUTURE, + local.span, + "non-binding `let` on a future", + None, + "consider awaiting the future or dropping explicitly with `std::mem::drop`" + ); + } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { + span_lint_and_help( + cx, + LET_UNDERSCORE_MUST_USE, + local.span, + "non-binding `let` on an expression with `#[must_use]` type", + None, + "consider explicitly using expression value", + ); + } else if is_must_use_func_call(cx, init) { + span_lint_and_help( + cx, + LET_UNDERSCORE_MUST_USE, + local.span, + "non-binding `let` on a result of a `#[must_use]` function", + None, + "consider explicitly using function result", + ); } } } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs deleted file mode 100644 index c455e1561b71..000000000000 --- a/clippy_lints/src/lib.register_all.rs +++ /dev/null @@ -1,368 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::all", Some("clippy_all"), vec![ - LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), - LintId::of(approx_const::APPROX_CONSTANT), - LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), - LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(attrs::DEPRECATED_CFG_ATTR), - LintId::of(attrs::DEPRECATED_SEMVER), - LintId::of(attrs::MISMATCHED_TARGET_OS), - LintId::of(attrs::USELESS_ATTRIBUTE), - LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), - LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), - LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), - LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF), - LintId::of(booleans::NONMINIMAL_BOOL), - LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), - LintId::of(borrow_deref_ref::BORROW_DEREF_REF), - LintId::of(box_default::BOX_DEFAULT), - LintId::of(casts::CAST_ABS_TO_UNSIGNED), - LintId::of(casts::CAST_ENUM_CONSTRUCTOR), - LintId::of(casts::CAST_ENUM_TRUNCATION), - LintId::of(casts::CAST_NAN_TO_INT), - LintId::of(casts::CAST_REF_TO_MUT), - LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), - LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), - LintId::of(casts::CHAR_LIT_AS_U8), - LintId::of(casts::FN_TO_NUMERIC_CAST), - LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(casts::UNNECESSARY_CAST), - LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(comparison_chain::COMPARISON_CHAIN), - LintId::of(copies::IFS_SAME_COND), - LintId::of(copies::IF_SAME_THEN_ELSE), - LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), - LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY), - LintId::of(dereference::EXPLICIT_AUTO_DEREF), - LintId::of(dereference::NEEDLESS_BORROW), - LintId::of(derivable_impls::DERIVABLE_IMPLS), - LintId::of(derive::DERIVE_HASH_XOR_EQ), - LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(disallowed_macros::DISALLOWED_MACROS), - LintId::of(disallowed_methods::DISALLOWED_METHODS), - LintId::of(disallowed_names::DISALLOWED_NAMES), - LintId::of(disallowed_types::DISALLOWED_TYPES), - LintId::of(doc::MISSING_SAFETY_DOC), - LintId::of(doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(double_parens::DOUBLE_PARENS), - LintId::of(drop_forget_ref::DROP_COPY), - LintId::of(drop_forget_ref::DROP_NON_DROP), - LintId::of(drop_forget_ref::DROP_REF), - LintId::of(drop_forget_ref::FORGET_COPY), - LintId::of(drop_forget_ref::FORGET_NON_DROP), - LintId::of(drop_forget_ref::FORGET_REF), - LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), - LintId::of(duplicate_mod::DUPLICATE_MOD), - LintId::of(entry::MAP_ENTRY), - LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(enum_variants::ENUM_VARIANT_NAMES), - LintId::of(enum_variants::MODULE_INCEPTION), - LintId::of(escape::BOXED_LOCAL), - LintId::of(eta_reduction::REDUNDANT_CLOSURE), - LintId::of(explicit_write::EXPLICIT_WRITE), - LintId::of(float_literal::EXCESSIVE_PRECISION), - LintId::of(format::USELESS_FORMAT), - LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), - LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), - LintId::of(format_args::UNUSED_FORMAT_SPECS), - LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), - LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), - LintId::of(formatting::POSSIBLE_MISSING_COMMA), - LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(from_over_into::FROM_OVER_INTO), - LintId::of(from_str_radix_10::FROM_STR_RADIX_10), - LintId::of(functions::DOUBLE_MUST_USE), - LintId::of(functions::MUST_USE_UNIT), - LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), - LintId::of(functions::RESULT_LARGE_ERR), - LintId::of(functions::RESULT_UNIT_ERR), - LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(if_let_mutex::IF_LET_MUTEX), - LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), - LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(infinite_iter::INFINITE_ITER), - LintId::of(inherent_to_string::INHERENT_TO_STRING), - LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), - LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(int_plus_one::INT_PLUS_ONE), - LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED), - LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(len_zero::COMPARISON_TO_EMPTY), - LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), - LintId::of(len_zero::LEN_ZERO), - LintId::of(let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(lifetimes::NEEDLESS_LIFETIMES), - LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::EXPLICIT_COUNTER_LOOP), - LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::ITER_NEXT_LOOP), - LintId::of(loops::MANUAL_FIND), - LintId::of(loops::MANUAL_FLATTEN), - LintId::of(loops::MANUAL_MEMCPY), - LintId::of(loops::MISSING_SPIN_LOOP), - LintId::of(loops::MUT_RANGE_BOUND), - LintId::of(loops::NEEDLESS_COLLECT), - LintId::of(loops::NEEDLESS_RANGE_LOOP), - LintId::of(loops::NEVER_LOOP), - LintId::of(loops::SAME_ITEM_PUSH), - LintId::of(loops::SINGLE_ELEMENT_LOOP), - LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(loops::WHILE_LET_LOOP), - LintId::of(loops::WHILE_LET_ON_ITERATOR), - LintId::of(main_recursion::MAIN_RECURSION), - LintId::of(manual_async_fn::MANUAL_ASYNC_FN), - LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_clamp::MANUAL_CLAMP), - LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), - LintId::of(manual_retain::MANUAL_RETAIN), - LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(matches::COLLAPSIBLE_MATCH), - LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), - LintId::of(matches::MANUAL_FILTER), - LintId::of(matches::MANUAL_MAP), - LintId::of(matches::MANUAL_UNWRAP_OR), - LintId::of(matches::MATCH_AS_REF), - LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), - LintId::of(matches::MATCH_OVERLAPPING_ARM), - LintId::of(matches::MATCH_REF_PATS), - LintId::of(matches::MATCH_SINGLE_BINDING), - LintId::of(matches::MATCH_STR_CASE_MISMATCH), - LintId::of(matches::NEEDLESS_MATCH), - LintId::of(matches::REDUNDANT_PATTERN_MATCHING), - LintId::of(matches::SINGLE_MATCH), - LintId::of(matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), - LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), - LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(methods::BIND_INSTEAD_OF_MAP), - LintId::of(methods::BYTES_COUNT_TO_LEN), - LintId::of(methods::BYTES_NTH), - LintId::of(methods::CHARS_LAST_CMP), - LintId::of(methods::CHARS_NEXT_CMP), - LintId::of(methods::CLONE_DOUBLE_REF), - LintId::of(methods::CLONE_ON_COPY), - LintId::of(methods::COLLAPSIBLE_STR_REPLACE), - LintId::of(methods::ERR_EXPECT), - LintId::of(methods::EXPECT_FUN_CALL), - LintId::of(methods::EXTEND_WITH_DRAIN), - LintId::of(methods::FILTER_MAP_IDENTITY), - LintId::of(methods::FILTER_NEXT), - LintId::of(methods::FLAT_MAP_IDENTITY), - LintId::of(methods::GET_FIRST), - LintId::of(methods::GET_LAST_WITH_LEN), - LintId::of(methods::INSPECT_FOR_EACH), - LintId::of(methods::INTO_ITER_ON_REF), - LintId::of(methods::IS_DIGIT_ASCII_RADIX), - LintId::of(methods::ITERATOR_STEP_BY_ZERO), - LintId::of(methods::ITER_CLONED_COLLECT), - LintId::of(methods::ITER_COUNT), - LintId::of(methods::ITER_KV_MAP), - LintId::of(methods::ITER_NEXT_SLICE), - LintId::of(methods::ITER_NTH), - LintId::of(methods::ITER_NTH_ZERO), - LintId::of(methods::ITER_OVEREAGER_CLONED), - LintId::of(methods::ITER_SKIP_NEXT), - LintId::of(methods::MANUAL_FILTER_MAP), - LintId::of(methods::MANUAL_FIND_MAP), - LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), - LintId::of(methods::MANUAL_SPLIT_ONCE), - LintId::of(methods::MANUAL_STR_REPEAT), - LintId::of(methods::MAP_CLONE), - LintId::of(methods::MAP_COLLECT_RESULT_UNIT), - LintId::of(methods::MAP_FLATTEN), - LintId::of(methods::MAP_IDENTITY), - LintId::of(methods::MUT_MUTEX_LOCK), - LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), - LintId::of(methods::NEEDLESS_OPTION_TAKE), - LintId::of(methods::NEEDLESS_SPLITN), - LintId::of(methods::NEW_RET_NO_SELF), - LintId::of(methods::NONSENSICAL_OPEN_OPTIONS), - LintId::of(methods::NO_EFFECT_REPLACE), - LintId::of(methods::OBFUSCATED_IF_ELSE), - LintId::of(methods::OK_EXPECT), - LintId::of(methods::OPTION_AS_REF_DEREF), - LintId::of(methods::OPTION_FILTER_MAP), - LintId::of(methods::OPTION_MAP_OR_NONE), - LintId::of(methods::OR_FUN_CALL), - LintId::of(methods::OR_THEN_UNWRAP), - LintId::of(methods::RANGE_ZIP_WITH_LEN), - LintId::of(methods::REPEAT_ONCE), - LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(methods::SEARCH_IS_SOME), - LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(methods::SINGLE_CHAR_ADD_STR), - LintId::of(methods::SINGLE_CHAR_PATTERN), - LintId::of(methods::SKIP_WHILE_NEXT), - LintId::of(methods::STRING_EXTEND_CHARS), - LintId::of(methods::SUSPICIOUS_MAP), - LintId::of(methods::SUSPICIOUS_SPLITN), - LintId::of(methods::SUSPICIOUS_TO_OWNED), - LintId::of(methods::UNINIT_ASSUMED_INIT), - LintId::of(methods::UNIT_HASH), - LintId::of(methods::UNNECESSARY_FILTER_MAP), - LintId::of(methods::UNNECESSARY_FIND_MAP), - LintId::of(methods::UNNECESSARY_FOLD), - LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(methods::UNNECESSARY_SORT_BY), - LintId::of(methods::UNNECESSARY_TO_OWNED), - LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), - LintId::of(methods::USELESS_ASREF), - LintId::of(methods::VEC_RESIZE_TO_ZERO), - LintId::of(methods::WRONG_SELF_CONVENTION), - LintId::of(methods::ZST_OFFSET), - LintId::of(minmax::MIN_MAX), - LintId::of(misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(misc::TOPLEVEL_REF_ARG), - LintId::of(misc::ZERO_PTR), - LintId::of(misc_early::BUILTIN_TYPE_SHADOW), - LintId::of(misc_early::DOUBLE_NEG), - LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), - LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(misc_early::REDUNDANT_PATTERN), - LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), - LintId::of(misc_early::ZERO_PREFIXED_LITERAL), - LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), - LintId::of(multi_assignments::MULTI_ASSIGNMENTS), - LintId::of(mut_key::MUTABLE_KEY_TYPE), - LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), - LintId::of(needless_bool::BOOL_COMPARISON), - LintId::of(needless_bool::NEEDLESS_BOOL), - LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), - LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), - LintId::of(needless_update::NEEDLESS_UPDATE), - LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), - LintId::of(neg_multiply::NEG_MULTIPLY), - LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(no_effect::NO_EFFECT), - LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), - LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), - LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), - LintId::of(operators::ABSURD_EXTREME_COMPARISONS), - LintId::of(operators::ASSIGN_OP_PATTERN), - LintId::of(operators::BAD_BIT_MASK), - LintId::of(operators::CMP_NAN), - LintId::of(operators::CMP_OWNED), - LintId::of(operators::DOUBLE_COMPARISONS), - LintId::of(operators::DURATION_SUBSEC), - LintId::of(operators::EQ_OP), - LintId::of(operators::ERASING_OP), - LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS), - LintId::of(operators::IDENTITY_OP), - LintId::of(operators::INEFFECTIVE_BIT_MASK), - LintId::of(operators::MISREFACTORED_ASSIGN_OP), - LintId::of(operators::MODULO_ONE), - LintId::of(operators::OP_REF), - LintId::of(operators::PTR_EQ), - LintId::of(operators::SELF_ASSIGNMENT), - LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE), - LintId::of(precedence::PRECEDENCE), - LintId::of(ptr::CMP_NULL), - LintId::of(ptr::INVALID_NULL_PTR_USAGE), - LintId::of(ptr::MUT_FROM_REF), - LintId::of(ptr::PTR_ARG), - LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(question_mark::QUESTION_MARK), - LintId::of(ranges::MANUAL_RANGE_CONTAINS), - LintId::of(ranges::REVERSED_EMPTY_RANGES), - LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), - LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC), - LintId::of(redundant_clone::REDUNDANT_CLONE), - LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(redundant_slicing::REDUNDANT_SLICING), - LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(reference::DEREF_ADDROF), - LintId::of(regex::INVALID_REGEX), - LintId::of(returns::LET_AND_RETURN), - LintId::of(returns::NEEDLESS_RETURN), - LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), - LintId::of(serde_api::SERDE_API_MISUSE), - LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(strings::TRIM_SPLIT_WHITESPACE), - LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), - LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(swap::ALMOST_SWAPPED), - LintId::of(swap::MANUAL_SWAP), - LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), - LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), - LintId::of(transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(transmute::TRANSMUTING_NULL), - LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), - LintId::of(transmute::USELESS_TRANSMUTE), - LintId::of(transmute::WRONG_TRANSMUTE), - LintId::of(types::BORROWED_BOX), - LintId::of(types::BOX_COLLECTION), - LintId::of(types::REDUNDANT_ALLOCATION), - LintId::of(types::TYPE_COMPLEXITY), - LintId::of(types::VEC_BOX), - LintId::of(unicode::INVISIBLE_CHARACTERS), - LintId::of(uninit_vec::UNINIT_VEC), - LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), - LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unit_types::UNIT_ARG), - LintId::of(unit_types::UNIT_CMP), - LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), - LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), - LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), - LintId::of(unused_unit::UNUSED_UNIT), - LintId::of(unwrap::PANICKING_UNWRAP), - LintId::of(unwrap::UNNECESSARY_UNWRAP), - LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(useless_conversion::USELESS_CONVERSION), - LintId::of(vec::USELESS_VEC), - LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), - LintId::of(write::PRINTLN_EMPTY_STRING), - LintId::of(write::PRINT_LITERAL), - LintId::of(write::PRINT_WITH_NEWLINE), - LintId::of(write::WRITELN_EMPTY_STRING), - LintId::of(write::WRITE_LITERAL), - LintId::of(write::WRITE_WITH_NEWLINE), - LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), -]) diff --git a/clippy_lints/src/lib.register_cargo.rs b/clippy_lints/src/lib.register_cargo.rs deleted file mode 100644 index c890523fe5ae..000000000000 --- a/clippy_lints/src/lib.register_cargo.rs +++ /dev/null @@ -1,11 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ - LintId::of(cargo::CARGO_COMMON_METADATA), - LintId::of(cargo::MULTIPLE_CRATE_VERSIONS), - LintId::of(cargo::NEGATIVE_FEATURE_NAMES), - LintId::of(cargo::REDUNDANT_FEATURE_NAMES), - LintId::of(cargo::WILDCARD_DEPENDENCIES), -]) diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs deleted file mode 100644 index 8be9dc4baf19..000000000000 --- a/clippy_lints/src/lib.register_complexity.rs +++ /dev/null @@ -1,111 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ - LintId::of(attrs::DEPRECATED_CFG_ATTR), - LintId::of(booleans::NONMINIMAL_BOOL), - LintId::of(borrow_deref_ref::BORROW_DEREF_REF), - LintId::of(casts::CHAR_LIT_AS_U8), - LintId::of(casts::UNNECESSARY_CAST), - LintId::of(dereference::EXPLICIT_AUTO_DEREF), - LintId::of(derivable_impls::DERIVABLE_IMPLS), - LintId::of(double_parens::DOUBLE_PARENS), - LintId::of(explicit_write::EXPLICIT_WRITE), - LintId::of(format::USELESS_FORMAT), - LintId::of(format_args::UNUSED_FORMAT_SPECS), - LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(int_plus_one::INT_PLUS_ONE), - LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(lifetimes::NEEDLESS_LIFETIMES), - LintId::of(loops::EXPLICIT_COUNTER_LOOP), - LintId::of(loops::MANUAL_FIND), - LintId::of(loops::MANUAL_FLATTEN), - LintId::of(loops::SINGLE_ELEMENT_LOOP), - LintId::of(loops::WHILE_LET_LOOP), - LintId::of(manual_clamp::MANUAL_CLAMP), - LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), - LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(matches::MANUAL_FILTER), - LintId::of(matches::MANUAL_UNWRAP_OR), - LintId::of(matches::MATCH_AS_REF), - LintId::of(matches::MATCH_SINGLE_BINDING), - LintId::of(matches::NEEDLESS_MATCH), - LintId::of(matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(methods::BIND_INSTEAD_OF_MAP), - LintId::of(methods::BYTES_COUNT_TO_LEN), - LintId::of(methods::CLONE_ON_COPY), - LintId::of(methods::FILTER_MAP_IDENTITY), - LintId::of(methods::FILTER_NEXT), - LintId::of(methods::FLAT_MAP_IDENTITY), - LintId::of(methods::GET_LAST_WITH_LEN), - LintId::of(methods::INSPECT_FOR_EACH), - LintId::of(methods::ITER_COUNT), - LintId::of(methods::ITER_KV_MAP), - LintId::of(methods::MANUAL_FILTER_MAP), - LintId::of(methods::MANUAL_FIND_MAP), - LintId::of(methods::MANUAL_SPLIT_ONCE), - LintId::of(methods::MAP_FLATTEN), - LintId::of(methods::MAP_IDENTITY), - LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), - LintId::of(methods::NEEDLESS_OPTION_TAKE), - LintId::of(methods::NEEDLESS_SPLITN), - LintId::of(methods::OPTION_AS_REF_DEREF), - LintId::of(methods::OPTION_FILTER_MAP), - LintId::of(methods::OR_THEN_UNWRAP), - LintId::of(methods::RANGE_ZIP_WITH_LEN), - LintId::of(methods::REPEAT_ONCE), - LintId::of(methods::SEARCH_IS_SOME), - LintId::of(methods::SKIP_WHILE_NEXT), - LintId::of(methods::UNNECESSARY_FILTER_MAP), - LintId::of(methods::UNNECESSARY_FIND_MAP), - LintId::of(methods::UNNECESSARY_SORT_BY), - LintId::of(methods::USELESS_ASREF), - LintId::of(misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), - LintId::of(misc_early::ZERO_PREFIXED_LITERAL), - LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), - LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), - LintId::of(needless_bool::BOOL_COMPARISON), - LintId::of(needless_bool::NEEDLESS_BOOL), - LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), - LintId::of(needless_update::NEEDLESS_UPDATE), - LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), - LintId::of(no_effect::NO_EFFECT), - LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), - LintId::of(operators::DOUBLE_COMPARISONS), - LintId::of(operators::DURATION_SUBSEC), - LintId::of(operators::IDENTITY_OP), - LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(precedence::PRECEDENCE), - LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(redundant_slicing::REDUNDANT_SLICING), - LintId::of(reference::DEREF_ADDROF), - LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), - LintId::of(swap::MANUAL_SWAP), - LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), - LintId::of(transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(transmute::USELESS_TRANSMUTE), - LintId::of(types::BORROWED_BOX), - LintId::of(types::TYPE_COMPLEXITY), - LintId::of(types::VEC_BOX), - LintId::of(unit_types::UNIT_ARG), - LintId::of(unwrap::UNNECESSARY_UNWRAP), - LintId::of(useless_conversion::USELESS_CONVERSION), - LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), -]) diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs deleted file mode 100644 index bb94037ec2e7..000000000000 --- a/clippy_lints/src/lib.register_correctness.rs +++ /dev/null @@ -1,78 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ - LintId::of(approx_const::APPROX_CONSTANT), - LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(attrs::DEPRECATED_SEMVER), - LintId::of(attrs::MISMATCHED_TARGET_OS), - LintId::of(attrs::USELESS_ATTRIBUTE), - LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), - LintId::of(casts::CAST_REF_TO_MUT), - LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), - LintId::of(copies::IFS_SAME_COND), - LintId::of(copies::IF_SAME_THEN_ELSE), - LintId::of(derive::DERIVE_HASH_XOR_EQ), - LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(drop_forget_ref::DROP_COPY), - LintId::of(drop_forget_ref::DROP_REF), - LintId::of(drop_forget_ref::FORGET_COPY), - LintId::of(drop_forget_ref::FORGET_REF), - LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), - LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), - LintId::of(formatting::POSSIBLE_MISSING_COMMA), - LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), - LintId::of(if_let_mutex::IF_LET_MUTEX), - LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(infinite_iter::INFINITE_ITER), - LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED), - LintId::of(let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(loops::ITER_NEXT_LOOP), - LintId::of(loops::NEVER_LOOP), - LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(matches::MATCH_STR_CASE_MISMATCH), - LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(methods::CLONE_DOUBLE_REF), - LintId::of(methods::ITERATOR_STEP_BY_ZERO), - LintId::of(methods::NONSENSICAL_OPEN_OPTIONS), - LintId::of(methods::SUSPICIOUS_SPLITN), - LintId::of(methods::UNINIT_ASSUMED_INIT), - LintId::of(methods::UNIT_HASH), - LintId::of(methods::VEC_RESIZE_TO_ZERO), - LintId::of(methods::ZST_OFFSET), - LintId::of(minmax::MIN_MAX), - LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - LintId::of(operators::ABSURD_EXTREME_COMPARISONS), - LintId::of(operators::BAD_BIT_MASK), - LintId::of(operators::CMP_NAN), - LintId::of(operators::EQ_OP), - LintId::of(operators::ERASING_OP), - LintId::of(operators::INEFFECTIVE_BIT_MASK), - LintId::of(operators::MODULO_ONE), - LintId::of(operators::SELF_ASSIGNMENT), - LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(ptr::INVALID_NULL_PTR_USAGE), - LintId::of(ptr::MUT_FROM_REF), - LintId::of(ranges::REVERSED_EMPTY_RANGES), - LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC), - LintId::of(regex::INVALID_REGEX), - LintId::of(serde_api::SERDE_API_MISUSE), - LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(swap::ALMOST_SWAPPED), - LintId::of(transmute::TRANSMUTING_NULL), - LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), - LintId::of(transmute::WRONG_TRANSMUTE), - LintId::of(unicode::INVISIBLE_CHARACTERS), - LintId::of(uninit_vec::UNINIT_VEC), - LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), - LintId::of(unit_types::UNIT_CMP), - LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), - LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), - LintId::of(unwrap::PANICKING_UNWRAP), -]) diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs deleted file mode 100644 index 40c94c6e8d33..000000000000 --- a/clippy_lints/src/lib.register_internal.rs +++ /dev/null @@ -1,22 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL), - LintId::of(utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS), - LintId::of(utils::internal_lints::if_chain_style::IF_CHAIN_STYLE), - LintId::of(utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL), - LintId::of(utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR), - LintId::of(utils::internal_lints::invalid_paths::INVALID_PATHS), - LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON), - LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT), - LintId::of(utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL), - LintId::of(utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA), - LintId::of(utils::internal_lints::produce_ice::PRODUCE_ICE), - LintId::of(utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH), -]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs deleted file mode 100644 index 800e3a876713..000000000000 --- a/clippy_lints/src/lib.register_lints.rs +++ /dev/null @@ -1,620 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_lints(&[ - #[cfg(feature = "internal")] - utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL, - #[cfg(feature = "internal")] - utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, - #[cfg(feature = "internal")] - utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS, - #[cfg(feature = "internal")] - utils::internal_lints::if_chain_style::IF_CHAIN_STYLE, - #[cfg(feature = "internal")] - utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL, - #[cfg(feature = "internal")] - utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR, - #[cfg(feature = "internal")] - utils::internal_lints::invalid_paths::INVALID_PATHS, - #[cfg(feature = "internal")] - utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON, - #[cfg(feature = "internal")] - utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT, - #[cfg(feature = "internal")] - utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal")] - utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, - #[cfg(feature = "internal")] - utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal")] - utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, - #[cfg(feature = "internal")] - utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, - #[cfg(feature = "internal")] - utils::internal_lints::produce_ice::PRODUCE_ICE, - #[cfg(feature = "internal")] - utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH, - almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, - approx_const::APPROX_CONSTANT, - as_conversions::AS_CONVERSIONS, - asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, - asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, - assertions_on_constants::ASSERTIONS_ON_CONSTANTS, - assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES, - async_yields_async::ASYNC_YIELDS_ASYNC, - attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON, - attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, - attrs::DEPRECATED_CFG_ATTR, - attrs::DEPRECATED_SEMVER, - attrs::EMPTY_LINE_AFTER_OUTER_ATTR, - attrs::INLINE_ALWAYS, - attrs::MISMATCHED_TARGET_OS, - attrs::USELESS_ATTRIBUTE, - await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE, - await_holding_invalid::AWAIT_HOLDING_LOCK, - await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, - blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, - bool_assert_comparison::BOOL_ASSERT_COMPARISON, - bool_to_int_with_if::BOOL_TO_INT_WITH_IF, - booleans::NONMINIMAL_BOOL, - booleans::OVERLY_COMPLEX_BOOL_EXPR, - borrow_deref_ref::BORROW_DEREF_REF, - box_default::BOX_DEFAULT, - cargo::CARGO_COMMON_METADATA, - cargo::MULTIPLE_CRATE_VERSIONS, - cargo::NEGATIVE_FEATURE_NAMES, - cargo::REDUNDANT_FEATURE_NAMES, - cargo::WILDCARD_DEPENDENCIES, - casts::AS_PTR_CAST_MUT, - casts::AS_UNDERSCORE, - casts::BORROW_AS_PTR, - casts::CAST_ABS_TO_UNSIGNED, - casts::CAST_ENUM_CONSTRUCTOR, - casts::CAST_ENUM_TRUNCATION, - casts::CAST_LOSSLESS, - casts::CAST_NAN_TO_INT, - casts::CAST_POSSIBLE_TRUNCATION, - casts::CAST_POSSIBLE_WRAP, - casts::CAST_PRECISION_LOSS, - casts::CAST_PTR_ALIGNMENT, - casts::CAST_REF_TO_MUT, - casts::CAST_SIGN_LOSS, - casts::CAST_SLICE_DIFFERENT_SIZES, - casts::CAST_SLICE_FROM_RAW_PARTS, - casts::CHAR_LIT_AS_U8, - casts::FN_TO_NUMERIC_CAST, - casts::FN_TO_NUMERIC_CAST_ANY, - casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - casts::PTR_AS_PTR, - casts::UNNECESSARY_CAST, - checked_conversions::CHECKED_CONVERSIONS, - cognitive_complexity::COGNITIVE_COMPLEXITY, - collapsible_if::COLLAPSIBLE_ELSE_IF, - collapsible_if::COLLAPSIBLE_IF, - comparison_chain::COMPARISON_CHAIN, - copies::BRANCHES_SHARING_CODE, - copies::IFS_SAME_COND, - copies::IF_SAME_THEN_ELSE, - copies::SAME_FUNCTIONS_IN_IF_CONDITION, - copy_iterator::COPY_ITERATOR, - crate_in_macro_def::CRATE_IN_MACRO_DEF, - create_dir::CREATE_DIR, - dbg_macro::DBG_MACRO, - default::DEFAULT_TRAIT_ACCESS, - default::FIELD_REASSIGN_WITH_DEFAULT, - default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY, - default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, - default_union_representation::DEFAULT_UNION_REPRESENTATION, - dereference::EXPLICIT_AUTO_DEREF, - dereference::EXPLICIT_DEREF_METHODS, - dereference::NEEDLESS_BORROW, - dereference::REF_BINDING_TO_REFERENCE, - derivable_impls::DERIVABLE_IMPLS, - derive::DERIVE_HASH_XOR_EQ, - derive::DERIVE_ORD_XOR_PARTIAL_ORD, - derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ, - derive::EXPL_IMPL_CLONE_ON_COPY, - derive::UNSAFE_DERIVE_DESERIALIZE, - disallowed_macros::DISALLOWED_MACROS, - disallowed_methods::DISALLOWED_METHODS, - disallowed_names::DISALLOWED_NAMES, - disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS, - disallowed_types::DISALLOWED_TYPES, - doc::DOC_LINK_WITH_QUOTES, - doc::DOC_MARKDOWN, - doc::MISSING_ERRORS_DOC, - doc::MISSING_PANICS_DOC, - doc::MISSING_SAFETY_DOC, - doc::NEEDLESS_DOCTEST_MAIN, - double_parens::DOUBLE_PARENS, - drop_forget_ref::DROP_COPY, - drop_forget_ref::DROP_NON_DROP, - drop_forget_ref::DROP_REF, - drop_forget_ref::FORGET_COPY, - drop_forget_ref::FORGET_NON_DROP, - drop_forget_ref::FORGET_REF, - drop_forget_ref::UNDROPPED_MANUALLY_DROPS, - duplicate_mod::DUPLICATE_MOD, - else_if_without_else::ELSE_IF_WITHOUT_ELSE, - empty_drop::EMPTY_DROP, - empty_enum::EMPTY_ENUM, - empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS, - entry::MAP_ENTRY, - enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, - enum_variants::ENUM_VARIANT_NAMES, - enum_variants::MODULE_INCEPTION, - enum_variants::MODULE_NAME_REPETITIONS, - equatable_if_let::EQUATABLE_IF_LET, - escape::BOXED_LOCAL, - eta_reduction::REDUNDANT_CLOSURE, - eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, - excessive_bools::STRUCT_EXCESSIVE_BOOLS, - exhaustive_items::EXHAUSTIVE_ENUMS, - exhaustive_items::EXHAUSTIVE_STRUCTS, - exit::EXIT, - explicit_write::EXPLICIT_WRITE, - fallible_impl_from::FALLIBLE_IMPL_FROM, - float_literal::EXCESSIVE_PRECISION, - float_literal::LOSSY_FLOAT_LITERAL, - floating_point_arithmetic::IMPRECISE_FLOPS, - floating_point_arithmetic::SUBOPTIMAL_FLOPS, - format::USELESS_FORMAT, - format_args::FORMAT_IN_FORMAT_ARGS, - format_args::TO_STRING_IN_FORMAT_ARGS, - format_args::UNINLINED_FORMAT_ARGS, - format_args::UNUSED_FORMAT_SPECS, - format_impl::PRINT_IN_FORMAT_IMPL, - format_impl::RECURSIVE_FORMAT_IMPL, - format_push_string::FORMAT_PUSH_STRING, - formatting::POSSIBLE_MISSING_COMMA, - formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, - formatting::SUSPICIOUS_ELSE_FORMATTING, - formatting::SUSPICIOUS_UNARY_OP_FORMATTING, - from_over_into::FROM_OVER_INTO, - from_str_radix_10::FROM_STR_RADIX_10, - functions::DOUBLE_MUST_USE, - functions::MUST_USE_CANDIDATE, - functions::MUST_USE_UNIT, - functions::NOT_UNSAFE_PTR_ARG_DEREF, - functions::RESULT_LARGE_ERR, - functions::RESULT_UNIT_ERR, - functions::TOO_MANY_ARGUMENTS, - functions::TOO_MANY_LINES, - future_not_send::FUTURE_NOT_SEND, - if_let_mutex::IF_LET_MUTEX, - if_not_else::IF_NOT_ELSE, - if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, - implicit_hasher::IMPLICIT_HASHER, - implicit_return::IMPLICIT_RETURN, - implicit_saturating_add::IMPLICIT_SATURATING_ADD, - implicit_saturating_sub::IMPLICIT_SATURATING_SUB, - inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, - index_refutable_slice::INDEX_REFUTABLE_SLICE, - indexing_slicing::INDEXING_SLICING, - indexing_slicing::OUT_OF_BOUNDS_INDEXING, - infinite_iter::INFINITE_ITER, - infinite_iter::MAYBE_INFINITE_ITER, - inherent_impl::MULTIPLE_INHERENT_IMPL, - inherent_to_string::INHERENT_TO_STRING, - inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, - init_numbered_fields::INIT_NUMBERED_FIELDS, - inline_fn_without_body::INLINE_FN_WITHOUT_BODY, - int_plus_one::INT_PLUS_ONE, - invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS, - invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED, - items_after_statements::ITEMS_AFTER_STATEMENTS, - iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR, - large_const_arrays::LARGE_CONST_ARRAYS, - large_enum_variant::LARGE_ENUM_VARIANT, - large_include_file::LARGE_INCLUDE_FILE, - large_stack_arrays::LARGE_STACK_ARRAYS, - len_zero::COMPARISON_TO_EMPTY, - len_zero::LEN_WITHOUT_IS_EMPTY, - len_zero::LEN_ZERO, - let_if_seq::USELESS_LET_IF_SEQ, - let_underscore::LET_UNDERSCORE_DROP, - let_underscore::LET_UNDERSCORE_LOCK, - let_underscore::LET_UNDERSCORE_MUST_USE, - lifetimes::EXTRA_UNUSED_LIFETIMES, - lifetimes::NEEDLESS_LIFETIMES, - literal_representation::DECIMAL_LITERAL_REPRESENTATION, - literal_representation::INCONSISTENT_DIGIT_GROUPING, - literal_representation::LARGE_DIGIT_GROUPS, - literal_representation::MISTYPED_LITERAL_SUFFIXES, - literal_representation::UNREADABLE_LITERAL, - literal_representation::UNUSUAL_BYTE_GROUPINGS, - loops::EMPTY_LOOP, - loops::EXPLICIT_COUNTER_LOOP, - loops::EXPLICIT_INTO_ITER_LOOP, - loops::EXPLICIT_ITER_LOOP, - loops::FOR_KV_MAP, - loops::ITER_NEXT_LOOP, - loops::MANUAL_FIND, - loops::MANUAL_FLATTEN, - loops::MANUAL_MEMCPY, - loops::MISSING_SPIN_LOOP, - loops::MUT_RANGE_BOUND, - loops::NEEDLESS_COLLECT, - loops::NEEDLESS_RANGE_LOOP, - loops::NEVER_LOOP, - loops::SAME_ITEM_PUSH, - loops::SINGLE_ELEMENT_LOOP, - loops::WHILE_IMMUTABLE_CONDITION, - loops::WHILE_LET_LOOP, - loops::WHILE_LET_ON_ITERATOR, - macro_use::MACRO_USE_IMPORTS, - main_recursion::MAIN_RECURSION, - manual_assert::MANUAL_ASSERT, - manual_async_fn::MANUAL_ASYNC_FN, - manual_bits::MANUAL_BITS, - manual_clamp::MANUAL_CLAMP, - manual_instant_elapsed::MANUAL_INSTANT_ELAPSED, - manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, - manual_rem_euclid::MANUAL_REM_EUCLID, - manual_retain::MANUAL_RETAIN, - manual_string_new::MANUAL_STRING_NEW, - manual_strip::MANUAL_STRIP, - map_unit_fn::OPTION_MAP_UNIT_FN, - map_unit_fn::RESULT_MAP_UNIT_FN, - match_result_ok::MATCH_RESULT_OK, - matches::COLLAPSIBLE_MATCH, - matches::INFALLIBLE_DESTRUCTURING_MATCH, - matches::MANUAL_FILTER, - matches::MANUAL_MAP, - matches::MANUAL_UNWRAP_OR, - matches::MATCH_AS_REF, - matches::MATCH_BOOL, - matches::MATCH_LIKE_MATCHES_MACRO, - matches::MATCH_ON_VEC_ITEMS, - matches::MATCH_OVERLAPPING_ARM, - matches::MATCH_REF_PATS, - matches::MATCH_SAME_ARMS, - matches::MATCH_SINGLE_BINDING, - matches::MATCH_STR_CASE_MISMATCH, - matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, - matches::MATCH_WILD_ERR_ARM, - matches::NEEDLESS_MATCH, - matches::REDUNDANT_PATTERN_MATCHING, - matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, - matches::SIGNIFICANT_DROP_IN_SCRUTINEE, - matches::SINGLE_MATCH, - matches::SINGLE_MATCH_ELSE, - matches::TRY_ERR, - matches::WILDCARD_ENUM_MATCH_ARM, - matches::WILDCARD_IN_OR_PATTERNS, - mem_forget::MEM_FORGET, - mem_replace::MEM_REPLACE_OPTION_WITH_NONE, - mem_replace::MEM_REPLACE_WITH_DEFAULT, - mem_replace::MEM_REPLACE_WITH_UNINIT, - methods::BIND_INSTEAD_OF_MAP, - methods::BYTES_COUNT_TO_LEN, - methods::BYTES_NTH, - methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - methods::CHARS_LAST_CMP, - methods::CHARS_NEXT_CMP, - methods::CLONED_INSTEAD_OF_COPIED, - methods::CLONE_DOUBLE_REF, - methods::CLONE_ON_COPY, - methods::CLONE_ON_REF_PTR, - methods::COLLAPSIBLE_STR_REPLACE, - methods::ERR_EXPECT, - methods::EXPECT_FUN_CALL, - methods::EXPECT_USED, - methods::EXTEND_WITH_DRAIN, - methods::FILETYPE_IS_FILE, - methods::FILTER_MAP_IDENTITY, - methods::FILTER_MAP_NEXT, - methods::FILTER_NEXT, - methods::FLAT_MAP_IDENTITY, - methods::FLAT_MAP_OPTION, - methods::FROM_ITER_INSTEAD_OF_COLLECT, - methods::GET_FIRST, - methods::GET_LAST_WITH_LEN, - methods::GET_UNWRAP, - methods::IMPLICIT_CLONE, - methods::INEFFICIENT_TO_STRING, - methods::INSPECT_FOR_EACH, - methods::INTO_ITER_ON_REF, - methods::IS_DIGIT_ASCII_RADIX, - methods::ITERATOR_STEP_BY_ZERO, - methods::ITER_CLONED_COLLECT, - methods::ITER_COUNT, - methods::ITER_KV_MAP, - methods::ITER_NEXT_SLICE, - methods::ITER_NTH, - methods::ITER_NTH_ZERO, - methods::ITER_ON_EMPTY_COLLECTIONS, - methods::ITER_ON_SINGLE_ITEMS, - methods::ITER_OVEREAGER_CLONED, - methods::ITER_SKIP_NEXT, - methods::ITER_WITH_DRAIN, - methods::MANUAL_FILTER_MAP, - methods::MANUAL_FIND_MAP, - methods::MANUAL_OK_OR, - methods::MANUAL_SATURATING_ARITHMETIC, - methods::MANUAL_SPLIT_ONCE, - methods::MANUAL_STR_REPEAT, - methods::MAP_CLONE, - methods::MAP_COLLECT_RESULT_UNIT, - methods::MAP_ERR_IGNORE, - methods::MAP_FLATTEN, - methods::MAP_IDENTITY, - methods::MAP_UNWRAP_OR, - methods::MUT_MUTEX_LOCK, - methods::NAIVE_BYTECOUNT, - methods::NEEDLESS_OPTION_AS_DEREF, - methods::NEEDLESS_OPTION_TAKE, - methods::NEEDLESS_SPLITN, - methods::NEW_RET_NO_SELF, - methods::NONSENSICAL_OPEN_OPTIONS, - methods::NO_EFFECT_REPLACE, - methods::OBFUSCATED_IF_ELSE, - methods::OK_EXPECT, - methods::OPTION_AS_REF_DEREF, - methods::OPTION_FILTER_MAP, - methods::OPTION_MAP_OR_NONE, - methods::OR_FUN_CALL, - methods::OR_THEN_UNWRAP, - methods::PATH_BUF_PUSH_OVERWRITE, - methods::RANGE_ZIP_WITH_LEN, - methods::REPEAT_ONCE, - methods::RESULT_MAP_OR_INTO_OPTION, - methods::SEARCH_IS_SOME, - methods::SHOULD_IMPLEMENT_TRAIT, - methods::SINGLE_CHAR_ADD_STR, - methods::SINGLE_CHAR_PATTERN, - methods::SKIP_WHILE_NEXT, - methods::STABLE_SORT_PRIMITIVE, - methods::STRING_EXTEND_CHARS, - methods::SUSPICIOUS_MAP, - methods::SUSPICIOUS_SPLITN, - methods::SUSPICIOUS_TO_OWNED, - methods::UNINIT_ASSUMED_INIT, - methods::UNIT_HASH, - methods::UNNECESSARY_FILTER_MAP, - methods::UNNECESSARY_FIND_MAP, - methods::UNNECESSARY_FOLD, - methods::UNNECESSARY_JOIN, - methods::UNNECESSARY_LAZY_EVALUATIONS, - methods::UNNECESSARY_SORT_BY, - methods::UNNECESSARY_TO_OWNED, - methods::UNWRAP_OR_ELSE_DEFAULT, - methods::UNWRAP_USED, - methods::USELESS_ASREF, - methods::VEC_RESIZE_TO_ZERO, - methods::VERBOSE_FILE_READS, - methods::WRONG_SELF_CONVENTION, - methods::ZST_OFFSET, - minmax::MIN_MAX, - misc::SHORT_CIRCUIT_STATEMENT, - misc::TOPLEVEL_REF_ARG, - misc::USED_UNDERSCORE_BINDING, - misc::ZERO_PTR, - misc_early::BUILTIN_TYPE_SHADOW, - misc_early::DOUBLE_NEG, - misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, - misc_early::MIXED_CASE_HEX_LITERALS, - misc_early::REDUNDANT_PATTERN, - misc_early::SEPARATED_LITERAL_SUFFIX, - misc_early::UNNEEDED_FIELD_PATTERN, - misc_early::UNNEEDED_WILDCARD_PATTERN, - misc_early::UNSEPARATED_LITERAL_SUFFIX, - misc_early::ZERO_PREFIXED_LITERAL, - mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER, - missing_const_for_fn::MISSING_CONST_FOR_FN, - missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, - missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, - missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, - missing_trait_methods::MISSING_TRAIT_METHODS, - mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION, - mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, - module_style::MOD_MODULE_FILES, - module_style::SELF_NAMED_MODULE_FILES, - multi_assignments::MULTI_ASSIGNMENTS, - mut_key::MUTABLE_KEY_TYPE, - mut_mut::MUT_MUT, - mut_reference::UNNECESSARY_MUT_PASSED, - mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, - mutex_atomic::MUTEX_ATOMIC, - mutex_atomic::MUTEX_INTEGER, - needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, - needless_bool::BOOL_COMPARISON, - needless_bool::NEEDLESS_BOOL, - needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, - needless_continue::NEEDLESS_CONTINUE, - needless_for_each::NEEDLESS_FOR_EACH, - needless_late_init::NEEDLESS_LATE_INIT, - needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS, - needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, - needless_question_mark::NEEDLESS_QUESTION_MARK, - needless_update::NEEDLESS_UPDATE, - neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, - neg_multiply::NEG_MULTIPLY, - new_without_default::NEW_WITHOUT_DEFAULT, - no_effect::NO_EFFECT, - no_effect::NO_EFFECT_UNDERSCORE_BINDING, - no_effect::UNNECESSARY_OPERATION, - non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, - non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, - non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, - non_expressive_names::MANY_SINGLE_CHAR_NAMES, - non_expressive_names::SIMILAR_NAMES, - non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, - non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY, - nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, - octal_escapes::OCTAL_ESCAPES, - only_used_in_recursion::ONLY_USED_IN_RECURSION, - operators::ABSURD_EXTREME_COMPARISONS, - operators::ARITHMETIC_SIDE_EFFECTS, - operators::ASSIGN_OP_PATTERN, - operators::BAD_BIT_MASK, - operators::CMP_NAN, - operators::CMP_OWNED, - operators::DOUBLE_COMPARISONS, - operators::DURATION_SUBSEC, - operators::EQ_OP, - operators::ERASING_OP, - operators::FLOAT_ARITHMETIC, - operators::FLOAT_CMP, - operators::FLOAT_CMP_CONST, - operators::FLOAT_EQUALITY_WITHOUT_ABS, - operators::IDENTITY_OP, - operators::INEFFECTIVE_BIT_MASK, - operators::INTEGER_ARITHMETIC, - operators::INTEGER_DIVISION, - operators::MISREFACTORED_ASSIGN_OP, - operators::MODULO_ARITHMETIC, - operators::MODULO_ONE, - operators::NEEDLESS_BITWISE_BOOL, - operators::OP_REF, - operators::PTR_EQ, - operators::SELF_ASSIGNMENT, - operators::VERBOSE_BIT_MASK, - option_env_unwrap::OPTION_ENV_UNWRAP, - option_if_let_else::OPTION_IF_LET_ELSE, - overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, - panic_in_result_fn::PANIC_IN_RESULT_FN, - panic_unimplemented::PANIC, - panic_unimplemented::TODO, - panic_unimplemented::UNIMPLEMENTED, - panic_unimplemented::UNREACHABLE, - partial_pub_fields::PARTIAL_PUB_FIELDS, - partialeq_ne_impl::PARTIALEQ_NE_IMPL, - partialeq_to_none::PARTIALEQ_TO_NONE, - pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, - pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, - pattern_type_mismatch::PATTERN_TYPE_MISMATCH, - precedence::PRECEDENCE, - ptr::CMP_NULL, - ptr::INVALID_NULL_PTR_USAGE, - ptr::MUT_FROM_REF, - ptr::PTR_ARG, - ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, - pub_use::PUB_USE, - question_mark::QUESTION_MARK, - ranges::MANUAL_RANGE_CONTAINS, - ranges::RANGE_MINUS_ONE, - ranges::RANGE_PLUS_ONE, - ranges::REVERSED_EMPTY_RANGES, - rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT, - read_zero_byte_vec::READ_ZERO_BYTE_VEC, - redundant_clone::REDUNDANT_CLONE, - redundant_closure_call::REDUNDANT_CLOSURE_CALL, - redundant_else::REDUNDANT_ELSE, - redundant_field_names::REDUNDANT_FIELD_NAMES, - redundant_pub_crate::REDUNDANT_PUB_CRATE, - redundant_slicing::DEREF_BY_SLICING, - redundant_slicing::REDUNDANT_SLICING, - redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, - ref_option_ref::REF_OPTION_REF, - reference::DEREF_ADDROF, - regex::INVALID_REGEX, - regex::TRIVIAL_REGEX, - return_self_not_must_use::RETURN_SELF_NOT_MUST_USE, - returns::LET_AND_RETURN, - returns::NEEDLESS_RETURN, - same_name_method::SAME_NAME_METHOD, - self_named_constructors::SELF_NAMED_CONSTRUCTORS, - semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, - serde_api::SERDE_API_MISUSE, - shadow::SHADOW_REUSE, - shadow::SHADOW_SAME, - shadow::SHADOW_UNRELATED, - single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, - single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, - size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, - slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - std_instead_of_core::ALLOC_INSTEAD_OF_CORE, - std_instead_of_core::STD_INSTEAD_OF_ALLOC, - std_instead_of_core::STD_INSTEAD_OF_CORE, - strings::STRING_ADD, - strings::STRING_ADD_ASSIGN, - strings::STRING_FROM_UTF8_AS_BYTES, - strings::STRING_LIT_AS_BYTES, - strings::STRING_SLICE, - strings::STRING_TO_STRING, - strings::STR_TO_STRING, - strings::TRIM_SPLIT_WHITESPACE, - strlen_on_c_strings::STRLEN_ON_C_STRINGS, - suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, - suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, - suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, - swap::ALMOST_SWAPPED, - swap::MANUAL_SWAP, - swap_ptr_to_ref::SWAP_PTR_TO_REF, - tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, - temporary_assignment::TEMPORARY_ASSIGNMENT, - to_digit_is_some::TO_DIGIT_IS_SOME, - trailing_empty_array::TRAILING_EMPTY_ARRAY, - trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, - trait_bounds::TYPE_REPETITION_IN_BOUNDS, - transmute::CROSSPOINTER_TRANSMUTE, - transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - transmute::TRANSMUTE_BYTES_TO_STR, - transmute::TRANSMUTE_FLOAT_TO_INT, - transmute::TRANSMUTE_INT_TO_BOOL, - transmute::TRANSMUTE_INT_TO_CHAR, - transmute::TRANSMUTE_INT_TO_FLOAT, - transmute::TRANSMUTE_NUM_TO_BYTES, - transmute::TRANSMUTE_PTR_TO_PTR, - transmute::TRANSMUTE_PTR_TO_REF, - transmute::TRANSMUTE_UNDEFINED_REPR, - transmute::TRANSMUTING_NULL, - transmute::UNSOUND_COLLECTION_TRANSMUTE, - transmute::USELESS_TRANSMUTE, - transmute::WRONG_TRANSMUTE, - types::BORROWED_BOX, - types::BOX_COLLECTION, - types::LINKEDLIST, - types::OPTION_OPTION, - types::RC_BUFFER, - types::RC_MUTEX, - types::REDUNDANT_ALLOCATION, - types::TYPE_COMPLEXITY, - types::VEC_BOX, - undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS, - unicode::INVISIBLE_CHARACTERS, - unicode::NON_ASCII_LITERAL, - unicode::UNICODE_NOT_NFC, - uninit_vec::UNINIT_VEC, - unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, - unit_types::LET_UNIT_VALUE, - unit_types::UNIT_ARG, - unit_types::UNIT_CMP, - unnamed_address::FN_ADDRESS_COMPARISONS, - unnamed_address::VTABLE_ADDRESS_COMPARISONS, - unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS, - unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, - unnecessary_wraps::UNNECESSARY_WRAPS, - unnested_or_patterns::UNNESTED_OR_PATTERNS, - unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - unused_async::UNUSED_ASYNC, - unused_io_amount::UNUSED_IO_AMOUNT, - unused_peekable::UNUSED_PEEKABLE, - unused_rounding::UNUSED_ROUNDING, - unused_self::UNUSED_SELF, - unused_unit::UNUSED_UNIT, - unwrap::PANICKING_UNWRAP, - unwrap::UNNECESSARY_UNWRAP, - unwrap_in_result::UNWRAP_IN_RESULT, - upper_case_acronyms::UPPER_CASE_ACRONYMS, - use_self::USE_SELF, - useless_conversion::USELESS_CONVERSION, - vec::USELESS_VEC, - vec_init_then_push::VEC_INIT_THEN_PUSH, - wildcard_imports::ENUM_GLOB_USE, - wildcard_imports::WILDCARD_IMPORTS, - write::PRINTLN_EMPTY_STRING, - write::PRINT_LITERAL, - write::PRINT_STDERR, - write::PRINT_STDOUT, - write::PRINT_WITH_NEWLINE, - write::USE_DEBUG, - write::WRITELN_EMPTY_STRING, - write::WRITE_LITERAL, - write::WRITE_WITH_NEWLINE, - zero_div_zero::ZERO_DIVIDED_BY_ZERO, - zero_sized_map_values::ZERO_SIZED_MAP_VALUES, -]) diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs deleted file mode 100644 index 65616d28d8f1..000000000000 --- a/clippy_lints/src/lib.register_nursery.rs +++ /dev/null @@ -1,39 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ - LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), - LintId::of(casts::AS_PTR_CAST_MUT), - LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), - LintId::of(copies::BRANCHES_SHARING_CODE), - LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), - LintId::of(equatable_if_let::EQUATABLE_IF_LET), - LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), - LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), - LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS), - LintId::of(future_not_send::FUTURE_NOT_SEND), - LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE), - LintId::of(let_if_seq::USELESS_LET_IF_SEQ), - LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), - LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS), - LintId::of(methods::ITER_ON_SINGLE_ITEMS), - LintId::of(methods::ITER_WITH_DRAIN), - LintId::of(methods::PATH_BUF_PUSH_OVERWRITE), - LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), - LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), - LintId::of(mutex_atomic::MUTEX_ATOMIC), - LintId::of(mutex_atomic::MUTEX_INTEGER), - LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), - LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), - LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), - LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), - LintId::of(regex::TRIVIAL_REGEX), - LintId::of(strings::STRING_LIT_AS_BYTES), - LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), - LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), - LintId::of(unused_peekable::UNUSED_PEEKABLE), - LintId::of(unused_rounding::UNUSED_ROUNDING), - LintId::of(use_self::USE_SELF), -]) diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs deleted file mode 100644 index 44e969585b50..000000000000 --- a/clippy_lints/src/lib.register_pedantic.rs +++ /dev/null @@ -1,104 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ - LintId::of(attrs::INLINE_ALWAYS), - LintId::of(casts::BORROW_AS_PTR), - LintId::of(casts::CAST_LOSSLESS), - LintId::of(casts::CAST_POSSIBLE_TRUNCATION), - LintId::of(casts::CAST_POSSIBLE_WRAP), - LintId::of(casts::CAST_PRECISION_LOSS), - LintId::of(casts::CAST_PTR_ALIGNMENT), - LintId::of(casts::CAST_SIGN_LOSS), - LintId::of(casts::PTR_AS_PTR), - LintId::of(checked_conversions::CHECKED_CONVERSIONS), - LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION), - LintId::of(copy_iterator::COPY_ITERATOR), - LintId::of(default::DEFAULT_TRAIT_ACCESS), - LintId::of(dereference::EXPLICIT_DEREF_METHODS), - LintId::of(dereference::REF_BINDING_TO_REFERENCE), - LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), - LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), - LintId::of(doc::DOC_LINK_WITH_QUOTES), - LintId::of(doc::DOC_MARKDOWN), - LintId::of(doc::MISSING_ERRORS_DOC), - LintId::of(doc::MISSING_PANICS_DOC), - LintId::of(empty_enum::EMPTY_ENUM), - LintId::of(enum_variants::MODULE_NAME_REPETITIONS), - LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), - LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), - LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(format_args::UNINLINED_FORMAT_ARGS), - LintId::of(functions::MUST_USE_CANDIDATE), - LintId::of(functions::TOO_MANY_LINES), - LintId::of(if_not_else::IF_NOT_ELSE), - LintId::of(implicit_hasher::IMPLICIT_HASHER), - LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - LintId::of(infinite_iter::MAYBE_INFINITE_ITER), - LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), - LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), - LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR), - LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(let_underscore::LET_UNDERSCORE_DROP), - LintId::of(literal_representation::LARGE_DIGIT_GROUPS), - LintId::of(literal_representation::UNREADABLE_LITERAL), - LintId::of(loops::EXPLICIT_INTO_ITER_LOOP), - LintId::of(loops::EXPLICIT_ITER_LOOP), - LintId::of(macro_use::MACRO_USE_IMPORTS), - LintId::of(manual_assert::MANUAL_ASSERT), - LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED), - LintId::of(manual_string_new::MANUAL_STRING_NEW), - LintId::of(matches::MATCH_BOOL), - LintId::of(matches::MATCH_ON_VEC_ITEMS), - LintId::of(matches::MATCH_SAME_ARMS), - LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), - LintId::of(matches::MATCH_WILD_ERR_ARM), - LintId::of(matches::SINGLE_MATCH_ELSE), - LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), - LintId::of(methods::CLONED_INSTEAD_OF_COPIED), - LintId::of(methods::FILTER_MAP_NEXT), - LintId::of(methods::FLAT_MAP_OPTION), - LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), - LintId::of(methods::IMPLICIT_CLONE), - LintId::of(methods::INEFFICIENT_TO_STRING), - LintId::of(methods::MANUAL_OK_OR), - LintId::of(methods::MAP_UNWRAP_OR), - LintId::of(methods::NAIVE_BYTECOUNT), - LintId::of(methods::STABLE_SORT_PRIMITIVE), - LintId::of(methods::UNNECESSARY_JOIN), - LintId::of(misc::USED_UNDERSCORE_BINDING), - LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER), - LintId::of(mut_mut::MUT_MUT), - LintId::of(needless_continue::NEEDLESS_CONTINUE), - LintId::of(needless_for_each::NEEDLESS_FOR_EACH), - LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), - LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING), - LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), - LintId::of(non_expressive_names::SIMILAR_NAMES), - LintId::of(operators::FLOAT_CMP), - LintId::of(operators::NEEDLESS_BITWISE_BOOL), - LintId::of(operators::VERBOSE_BIT_MASK), - LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), - LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), - LintId::of(ranges::RANGE_MINUS_ONE), - LintId::of(ranges::RANGE_PLUS_ONE), - LintId::of(redundant_else::REDUNDANT_ELSE), - LintId::of(ref_option_ref::REF_OPTION_REF), - LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), - LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), - LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), - LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), - LintId::of(types::LINKEDLIST), - LintId::of(types::OPTION_OPTION), - LintId::of(unicode::UNICODE_NOT_NFC), - LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), - LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), - LintId::of(unused_async::UNUSED_ASYNC), - LintId::of(unused_self::UNUSED_SELF), - LintId::of(wildcard_imports::ENUM_GLOB_USE), - LintId::of(wildcard_imports::WILDCARD_IMPORTS), - LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES), -]) diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs deleted file mode 100644 index 8e927470e02f..000000000000 --- a/clippy_lints/src/lib.register_perf.rs +++ /dev/null @@ -1,34 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ - LintId::of(box_default::BOX_DEFAULT), - LintId::of(entry::MAP_ENTRY), - LintId::of(escape::BOXED_LOCAL), - LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), - LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), - LintId::of(functions::RESULT_LARGE_ERR), - LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(loops::MANUAL_MEMCPY), - LintId::of(loops::MISSING_SPIN_LOOP), - LintId::of(loops::NEEDLESS_COLLECT), - LintId::of(manual_retain::MANUAL_RETAIN), - LintId::of(methods::COLLAPSIBLE_STR_REPLACE), - LintId::of(methods::EXPECT_FUN_CALL), - LintId::of(methods::EXTEND_WITH_DRAIN), - LintId::of(methods::ITER_NTH), - LintId::of(methods::ITER_OVEREAGER_CLONED), - LintId::of(methods::MANUAL_STR_REPEAT), - LintId::of(methods::OR_FUN_CALL), - LintId::of(methods::SINGLE_CHAR_PATTERN), - LintId::of(methods::UNNECESSARY_TO_OWNED), - LintId::of(operators::CMP_OWNED), - LintId::of(redundant_clone::REDUNDANT_CLONE), - LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(types::BOX_COLLECTION), - LintId::of(types::REDUNDANT_ALLOCATION), - LintId::of(vec::USELESS_VEC), - LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), -]) diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs deleted file mode 100644 index f62d57af5b47..000000000000 --- a/clippy_lints/src/lib.register_restriction.rs +++ /dev/null @@ -1,90 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ - LintId::of(as_conversions::AS_CONVERSIONS), - LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), - LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), - LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES), - LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON), - LintId::of(casts::AS_UNDERSCORE), - LintId::of(casts::FN_TO_NUMERIC_CAST_ANY), - LintId::of(create_dir::CREATE_DIR), - LintId::of(dbg_macro::DBG_MACRO), - LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), - LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION), - LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), - LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), - LintId::of(empty_drop::EMPTY_DROP), - LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), - LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), - LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), - LintId::of(exit::EXIT), - LintId::of(float_literal::LOSSY_FLOAT_LITERAL), - LintId::of(format_push_string::FORMAT_PUSH_STRING), - LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE), - LintId::of(implicit_return::IMPLICIT_RETURN), - LintId::of(indexing_slicing::INDEXING_SLICING), - LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), - LintId::of(large_include_file::LARGE_INCLUDE_FILE), - LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), - LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), - LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), - LintId::of(matches::TRY_ERR), - LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), - LintId::of(mem_forget::MEM_FORGET), - LintId::of(methods::CLONE_ON_REF_PTR), - LintId::of(methods::EXPECT_USED), - LintId::of(methods::FILETYPE_IS_FILE), - LintId::of(methods::GET_UNWRAP), - LintId::of(methods::MAP_ERR_IGNORE), - LintId::of(methods::UNWRAP_USED), - LintId::of(methods::VERBOSE_FILE_READS), - LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX), - LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), - LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), - LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), - LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), - LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), - LintId::of(missing_trait_methods::MISSING_TRAIT_METHODS), - LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION), - LintId::of(module_style::MOD_MODULE_FILES), - LintId::of(module_style::SELF_NAMED_MODULE_FILES), - LintId::of(operators::ARITHMETIC_SIDE_EFFECTS), - LintId::of(operators::FLOAT_ARITHMETIC), - LintId::of(operators::FLOAT_CMP_CONST), - LintId::of(operators::INTEGER_ARITHMETIC), - LintId::of(operators::INTEGER_DIVISION), - LintId::of(operators::MODULO_ARITHMETIC), - LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), - LintId::of(panic_unimplemented::PANIC), - LintId::of(panic_unimplemented::TODO), - LintId::of(panic_unimplemented::UNIMPLEMENTED), - LintId::of(panic_unimplemented::UNREACHABLE), - LintId::of(partial_pub_fields::PARTIAL_PUB_FIELDS), - LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), - LintId::of(pub_use::PUB_USE), - LintId::of(redundant_slicing::DEREF_BY_SLICING), - LintId::of(same_name_method::SAME_NAME_METHOD), - LintId::of(shadow::SHADOW_REUSE), - LintId::of(shadow::SHADOW_SAME), - LintId::of(shadow::SHADOW_UNRELATED), - LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES), - LintId::of(std_instead_of_core::ALLOC_INSTEAD_OF_CORE), - LintId::of(std_instead_of_core::STD_INSTEAD_OF_ALLOC), - LintId::of(std_instead_of_core::STD_INSTEAD_OF_CORE), - LintId::of(strings::STRING_ADD), - LintId::of(strings::STRING_SLICE), - LintId::of(strings::STRING_TO_STRING), - LintId::of(strings::STR_TO_STRING), - LintId::of(types::RC_BUFFER), - LintId::of(types::RC_MUTEX), - LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS), - LintId::of(unicode::NON_ASCII_LITERAL), - LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), - LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), - LintId::of(write::PRINT_STDERR), - LintId::of(write::PRINT_STDOUT), - LintId::of(write::USE_DEBUG), -]) diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs deleted file mode 100644 index 3312f5648552..000000000000 --- a/clippy_lints/src/lib.register_style.rs +++ /dev/null @@ -1,131 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::style", Some("clippy_style"), vec![ - LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), - LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), - LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF), - LintId::of(casts::FN_TO_NUMERIC_CAST), - LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(comparison_chain::COMPARISON_CHAIN), - LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY), - LintId::of(dereference::NEEDLESS_BORROW), - LintId::of(disallowed_macros::DISALLOWED_MACROS), - LintId::of(disallowed_methods::DISALLOWED_METHODS), - LintId::of(disallowed_names::DISALLOWED_NAMES), - LintId::of(disallowed_types::DISALLOWED_TYPES), - LintId::of(doc::MISSING_SAFETY_DOC), - LintId::of(doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(enum_variants::ENUM_VARIANT_NAMES), - LintId::of(enum_variants::MODULE_INCEPTION), - LintId::of(eta_reduction::REDUNDANT_CLOSURE), - LintId::of(float_literal::EXCESSIVE_PRECISION), - LintId::of(from_over_into::FROM_OVER_INTO), - LintId::of(from_str_radix_10::FROM_STR_RADIX_10), - LintId::of(functions::DOUBLE_MUST_USE), - LintId::of(functions::MUST_USE_UNIT), - LintId::of(functions::RESULT_UNIT_ERR), - LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), - LintId::of(inherent_to_string::INHERENT_TO_STRING), - LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), - LintId::of(len_zero::COMPARISON_TO_EMPTY), - LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), - LintId::of(len_zero::LEN_ZERO), - LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::NEEDLESS_RANGE_LOOP), - LintId::of(loops::SAME_ITEM_PUSH), - LintId::of(loops::WHILE_LET_ON_ITERATOR), - LintId::of(main_recursion::MAIN_RECURSION), - LintId::of(manual_async_fn::MANUAL_ASYNC_FN), - LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(matches::COLLAPSIBLE_MATCH), - LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), - LintId::of(matches::MANUAL_MAP), - LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), - LintId::of(matches::MATCH_OVERLAPPING_ARM), - LintId::of(matches::MATCH_REF_PATS), - LintId::of(matches::REDUNDANT_PATTERN_MATCHING), - LintId::of(matches::SINGLE_MATCH), - LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), - LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), - LintId::of(methods::BYTES_NTH), - LintId::of(methods::CHARS_LAST_CMP), - LintId::of(methods::CHARS_NEXT_CMP), - LintId::of(methods::ERR_EXPECT), - LintId::of(methods::GET_FIRST), - LintId::of(methods::INTO_ITER_ON_REF), - LintId::of(methods::IS_DIGIT_ASCII_RADIX), - LintId::of(methods::ITER_CLONED_COLLECT), - LintId::of(methods::ITER_NEXT_SLICE), - LintId::of(methods::ITER_NTH_ZERO), - LintId::of(methods::ITER_SKIP_NEXT), - LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), - LintId::of(methods::MAP_CLONE), - LintId::of(methods::MAP_COLLECT_RESULT_UNIT), - LintId::of(methods::MUT_MUTEX_LOCK), - LintId::of(methods::NEW_RET_NO_SELF), - LintId::of(methods::OBFUSCATED_IF_ELSE), - LintId::of(methods::OK_EXPECT), - LintId::of(methods::OPTION_MAP_OR_NONE), - LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(methods::SINGLE_CHAR_ADD_STR), - LintId::of(methods::STRING_EXTEND_CHARS), - LintId::of(methods::UNNECESSARY_FOLD), - LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), - LintId::of(methods::WRONG_SELF_CONVENTION), - LintId::of(misc::TOPLEVEL_REF_ARG), - LintId::of(misc::ZERO_PTR), - LintId::of(misc_early::BUILTIN_TYPE_SHADOW), - LintId::of(misc_early::DOUBLE_NEG), - LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), - LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(misc_early::REDUNDANT_PATTERN), - LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), - LintId::of(neg_multiply::NEG_MULTIPLY), - LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), - LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), - LintId::of(operators::ASSIGN_OP_PATTERN), - LintId::of(operators::OP_REF), - LintId::of(operators::PTR_EQ), - LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE), - LintId::of(ptr::CMP_NULL), - LintId::of(ptr::PTR_ARG), - LintId::of(question_mark::QUESTION_MARK), - LintId::of(ranges::MANUAL_RANGE_CONTAINS), - LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(returns::LET_AND_RETURN), - LintId::of(returns::NEEDLESS_RETURN), - LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), - LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(strings::TRIM_SPLIT_WHITESPACE), - LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), - LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(unused_unit::UNUSED_UNIT), - LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(write::PRINTLN_EMPTY_STRING), - LintId::of(write::PRINT_LITERAL), - LintId::of(write::PRINT_WITH_NEWLINE), - LintId::of(write::WRITELN_EMPTY_STRING), - LintId::of(write::WRITE_LITERAL), - LintId::of(write::WRITE_WITH_NEWLINE), -]) diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs deleted file mode 100644 index b70c4bb73e57..000000000000 --- a/clippy_lints/src/lib.register_suspicious.rs +++ /dev/null @@ -1,38 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![ - LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), - LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), - LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), - LintId::of(casts::CAST_ABS_TO_UNSIGNED), - LintId::of(casts::CAST_ENUM_CONSTRUCTOR), - LintId::of(casts::CAST_ENUM_TRUNCATION), - LintId::of(casts::CAST_NAN_TO_INT), - LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), - LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), - LintId::of(drop_forget_ref::DROP_NON_DROP), - LintId::of(drop_forget_ref::FORGET_NON_DROP), - LintId::of(duplicate_mod::DUPLICATE_MOD), - LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), - LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::MUT_RANGE_BOUND), - LintId::of(methods::NO_EFFECT_REPLACE), - LintId::of(methods::SUSPICIOUS_MAP), - LintId::of(methods::SUSPICIOUS_TO_OWNED), - LintId::of(multi_assignments::MULTI_ASSIGNMENTS), - LintId::of(mut_key::MUTABLE_KEY_TYPE), - LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS), - LintId::of(operators::MISREFACTORED_ASSIGN_OP), - LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), - LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), -]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1307096b28d7..b481314abedc 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -32,8 +32,8 @@ extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_analysis; -extern crate rustc_hir_typeck; extern crate rustc_hir_pretty; +extern crate rustc_hir_typeck; extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; @@ -47,122 +47,24 @@ extern crate rustc_trait_selection; #[macro_use] extern crate clippy_utils; +#[macro_use] +extern crate declare_clippy_lint; + +use std::io; +use std::path::PathBuf; use clippy_utils::parse_msrv; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::LintId; +use rustc_lint::{Lint, LintId}; use rustc_semver::RustcVersion; use rustc_session::Session; -/// Macro used to declare a Clippy lint. -/// -/// Every lint declaration consists of 4 parts: -/// -/// 1. The documentation, which is used for the website -/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. -/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or -/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. -/// 4. The `description` that contains a short explanation on what's wrong with code where the -/// lint is triggered. -/// -/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are -/// enabled by default. As said in the README.md of this repository, if the lint level mapping -/// changes, please update README.md. -/// -/// # Example -/// -/// ``` -/// #![feature(rustc_private)] -/// extern crate rustc_session; -/// use rustc_session::declare_tool_lint; -/// use clippy_lints::declare_clippy_lint; -/// -/// declare_clippy_lint! { -/// /// ### What it does -/// /// Checks for ... (describe what the lint matches). -/// /// -/// /// ### Why is this bad? -/// /// Supply the reason for linting the code. -/// /// -/// /// ### Example -/// /// ```rust -/// /// Insert a short example of code that triggers the lint -/// /// ``` -/// /// -/// /// Use instead: -/// /// ```rust -/// /// Insert a short example of improved code that doesn't trigger the lint -/// /// ``` -/// pub LINT_NAME, -/// pedantic, -/// "description" -/// } -/// ``` -/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints -#[macro_export] -macro_rules! declare_clippy_lint { - { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; -} - #[cfg(feature = "internal")] pub mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; +mod declared_lints; mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` @@ -231,6 +133,7 @@ mod format_impl; mod format_push_string; mod formatting; mod from_over_into; +mod from_raw_with_void_ptr; mod from_str_radix_10; mod functions; mod future_not_send; @@ -249,6 +152,7 @@ mod inherent_impl; mod inherent_to_string; mod init_numbered_fields; mod inline_fn_without_body; +mod instant_subtraction; mod int_plus_one; mod invalid_upcast_comparisons; mod invalid_utf8_in_unchecked; @@ -270,7 +174,8 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; -mod manual_instant_elapsed; +mod manual_is_ascii_check; +mod manual_let_else; mod manual_non_exhaustive; mod manual_rem_euclid; mod manual_retain; @@ -365,6 +270,7 @@ mod strings; mod strlen_on_c_strings; mod suspicious_operation_groupings; mod suspicious_trait_impl; +mod suspicious_xor_used_as_pow; mod swap; mod swap_ptr_to_ref; mod tabs_in_doc_comments; @@ -404,8 +310,8 @@ mod zero_div_zero; mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` -pub use crate::utils::conf::Conf; use crate::utils::conf::{format_error, TryConf}; +pub use crate::utils::conf::{lookup_conf_file, Conf}; /// Register all pre expansion lints /// @@ -462,8 +368,8 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option { } #[doc(hidden)] -pub fn read_conf(sess: &Session) -> Conf { - let file_name = match utils::conf::lookup_conf_file() { +pub fn read_conf(sess: &Session, path: &io::Result>) -> Conf { + let file_name = match path { Ok(Some(path)) => path, Ok(None) => return Conf::default(), Err(error) => { @@ -473,7 +379,7 @@ pub fn read_conf(sess: &Session) -> Conf { }, }; - let TryConf { conf, errors, warnings } = utils::conf::read(&file_name); + let TryConf { conf, errors, warnings } = utils::conf::read(file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { sess.err(format!( @@ -495,31 +401,121 @@ pub fn read_conf(sess: &Session) -> Conf { conf } +#[derive(Default)] +struct RegistrationGroups { + all: Vec, + cargo: Vec, + complexity: Vec, + correctness: Vec, + nursery: Vec, + pedantic: Vec, + perf: Vec, + restriction: Vec, + style: Vec, + suspicious: Vec, + #[cfg(feature = "internal")] + internal: Vec, +} + +impl RegistrationGroups { + #[rustfmt::skip] + fn register(self, store: &mut rustc_lint::LintStore) { + store.register_group(true, "clippy::all", Some("clippy_all"), self.all); + store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo); + store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity); + store.register_group(true, "clippy::correctness", Some("clippy_correctness"), self.correctness); + store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery); + store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic); + store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf); + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction); + store.register_group(true, "clippy::style", Some("clippy_style"), self.style); + store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious); + #[cfg(feature = "internal")] + store.register_group(true, "clippy::internal", Some("clippy_internal"), self.internal); + } +} + +#[derive(Copy, Clone)] +pub(crate) enum LintCategory { + Cargo, + Complexity, + Correctness, + Nursery, + Pedantic, + Perf, + Restriction, + Style, + Suspicious, + #[cfg(feature = "internal")] + Internal, +} +#[allow(clippy::enum_glob_use)] +use LintCategory::*; + +impl LintCategory { + fn is_all(self) -> bool { + matches!(self, Correctness | Suspicious | Style | Complexity | Perf) + } + + fn group(self, groups: &mut RegistrationGroups) -> &mut Vec { + match self { + Cargo => &mut groups.cargo, + Complexity => &mut groups.complexity, + Correctness => &mut groups.correctness, + Nursery => &mut groups.nursery, + Pedantic => &mut groups.pedantic, + Perf => &mut groups.perf, + Restriction => &mut groups.restriction, + Style => &mut groups.style, + Suspicious => &mut groups.suspicious, + #[cfg(feature = "internal")] + Internal => &mut groups.internal, + } + } +} + +pub(crate) struct LintInfo { + /// Double reference to maintain pointer equality + lint: &'static &'static Lint, + category: LintCategory, + explanation: &'static str, +} + +pub fn explain(name: &str) { + let target = format!("clippy::{}", name.to_ascii_uppercase()); + match declared_lints::LINTS.iter().find(|info| info.lint.name == target) { + Some(info) => print!("{}", info.explanation), + None => println!("unknown lint: {name}"), + } +} + +fn register_categories(store: &mut rustc_lint::LintStore) { + let mut groups = RegistrationGroups::default(); + + for LintInfo { lint, category, .. } in declared_lints::LINTS { + if category.is_all() { + groups.all.push(LintId::of(lint)); + } + + category.group(&mut groups).push(LintId::of(lint)); + } + + let lints: Vec<&'static Lint> = declared_lints::LINTS.iter().map(|info| *info.lint).collect(); + + store.register_lints(&lints); + groups.register(store); +} + /// Register all lints and lint groups with the rustc plugin registry /// /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { register_removed_non_tool_lints(store); + register_categories(store); include!("lib.deprecated.rs"); - include!("lib.register_lints.rs"); - include!("lib.register_restriction.rs"); - include!("lib.register_pedantic.rs"); - - #[cfg(feature = "internal")] - include!("lib.register_internal.rs"); - - include!("lib.register_all.rs"); - include!("lib.register_style.rs"); - include!("lib.register_complexity.rs"); - include!("lib.register_correctness.rs"); - include!("lib.register_suspicious.rs"); - include!("lib.register_perf.rs"); - include!("lib.register_cargo.rs"); - include!("lib.register_nursery.rs"); - #[cfg(feature = "internal")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { @@ -614,6 +610,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv))); + let matches_for_let_else = conf.matches_for_let_else; + store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else))); store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv))); @@ -735,7 +733,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let max_trait_bounds = conf.max_trait_bounds; store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds))); store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); - store.register_late_pass(|_| Box::new(mut_key::MutableKeyType)); + let ignore_interior_mutability = conf.ignore_interior_mutability.clone(); + store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone()))); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new())); @@ -794,10 +793,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); store.register_early_pass(|| Box::new(as_conversions::AsConversions)); store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); - store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports)); + store.register_early_pass(|| Box::::default()); let max_fn_params_bools = conf.max_fn_params_bools; let max_struct_bools = conf.max_struct_bools; - store.register_early_pass(move || { + store.register_late_pass(move |_| { Box::new(excessive_bools::ExcessiveBools::new( max_struct_bools, max_fn_params_bools, @@ -879,13 +878,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::::default()); let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); + let allow_print_in_tests = conf.allow_print_in_tests; + store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move |_| { Box::new(cargo::Cargo { ignore_publish: cargo_ignore_publish, }) }); - store.register_late_pass(|_| Box::::default()); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); @@ -908,7 +908,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed)); + store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); @@ -919,6 +919,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields)); store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); + store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); + store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); + store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 54c316358a14..0bb9eca15287 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; @@ -152,6 +152,7 @@ fn check_fn_inner<'tcx>( .params .iter() .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); + for typ in types { for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) { if pred.origin == PredicateOrigin::WhereClause { @@ -188,15 +189,30 @@ fn check_fn_inner<'tcx>( } } } - if could_use_elision(cx, decl, body, trait_sig, generics.params) { - span_lint( + + if let Some(elidable_lts) = could_use_elision(cx, decl, body, trait_sig, generics.params) { + let lts = elidable_lts + .iter() + // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a + // `Node::GenericParam`. + .filter_map(|&(def_id, _)| cx.tcx.hir().get_by_def_id(def_id).ident()) + .map(|ident| ident.to_string()) + .collect::>() + .join(", "); + + span_lint_and_then( cx, NEEDLESS_LIFETIMES, span.with_hi(decl.output.span().hi()), - "explicit lifetimes given in parameter types where they could be elided \ - (or replaced with `'_` if needed by type declaration)", + &format!("the following explicit lifetimes could be elided: {lts}"), + |diag| { + if let Some(span) = elidable_lts.iter().find_map(|&(_, span)| span) { + diag.span_help(span, "replace with `'_` in generic arguments such as here"); + } + }, ); } + if report_extra_lifetimes { self::report_extra_lifetimes(cx, decl, generics); } @@ -227,7 +243,7 @@ fn could_use_elision<'tcx>( body: Option, trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], -) -> bool { +) -> Option)>> { // There are two scenarios where elision works: // * no output references, all input references have different LT // * output references, exactly one input reference with same LT @@ -254,7 +270,7 @@ fn could_use_elision<'tcx>( } if input_visitor.abort() || output_visitor.abort() { - return false; + return None; } let input_lts = input_visitor.lts; @@ -262,7 +278,7 @@ fn could_use_elision<'tcx>( if let Some(trait_sig) = trait_sig { if explicit_self_type(cx, func, trait_sig.first().copied()) { - return false; + return None; } } @@ -271,7 +287,7 @@ fn could_use_elision<'tcx>( let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); if explicit_self_type(cx, func, first_ident) { - return false; + return None; } let mut checker = BodyLifetimeChecker { @@ -279,14 +295,14 @@ fn could_use_elision<'tcx>( }; checker.visit_expr(body.value); if checker.lifetimes_used_in_body { - return false; + return None; } } // check for lifetimes from higher scopes for lt in input_lts.iter().chain(output_lts.iter()) { if !allowed_lts.contains(lt) { - return false; + return None; } } @@ -302,48 +318,45 @@ fn could_use_elision<'tcx>( for lt in input_visitor.nested_elision_site_lts { if let RefLt::Named(def_id) = lt { if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return false; + return None; } } } for lt in output_visitor.nested_elision_site_lts { if let RefLt::Named(def_id) = lt { if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return false; + return None; } } } } - // no input lifetimes? easy case! - if input_lts.is_empty() { - false - } else if output_lts.is_empty() { - // no output lifetimes, check distinctness of input lifetimes + // A lifetime can be newly elided if: + // - It occurs only once among the inputs. + // - If there are multiple input lifetimes, then the newly elided lifetime does not occur among the + // outputs (because eliding such an lifetime would create an ambiguity). + let elidable_lts = named_lifetime_occurrences(&input_lts) + .into_iter() + .filter_map(|(def_id, occurrences)| { + if occurrences == 1 && (input_lts.len() == 1 || !output_lts.contains(&RefLt::Named(def_id))) { + Some(( + def_id, + input_visitor + .lifetime_generic_arg_spans + .get(&def_id) + .or_else(|| output_visitor.lifetime_generic_arg_spans.get(&def_id)) + .copied(), + )) + } else { + None + } + }) + .collect::>(); - // only unnamed and static, ok - let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static); - if unnamed_and_static { - return false; - } - // we have no output reference, so we only need all distinct lifetimes - input_lts.len() == unique_lifetimes(&input_lts) + if elidable_lts.is_empty() { + None } else { - // we have output references, so we need one input reference, - // and all output lifetimes must be the same - if unique_lifetimes(&output_lts) > 1 { - return false; - } - if input_lts.len() == 1 { - match (&input_lts[0], &output_lts[0]) { - (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true, - (&RefLt::Named(_), &RefLt::Unnamed) => true, - _ => false, /* already elided, different named lifetimes - * or something static going on */ - } - } else { - false - } + Some(elidable_lts) } } @@ -359,16 +372,31 @@ fn allowed_lts_from(tcx: TyCtxt<'_>, named_generics: &[GenericParam<'_>]) -> FxH allowed_lts } -/// Number of unique lifetimes in the given vector. +/// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve +/// relative order. #[must_use] -fn unique_lifetimes(lts: &[RefLt]) -> usize { - lts.iter().collect::>().len() +fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> { + let mut occurrences = Vec::new(); + for lt in lts { + if let &RefLt::Named(curr_def_id) = lt { + if let Some(pair) = occurrences + .iter_mut() + .find(|(prev_def_id, _)| *prev_def_id == curr_def_id) + { + pair.1 += 1; + } else { + occurrences.push((curr_def_id, 1)); + } + } + } + occurrences } /// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, lts: Vec, + lifetime_generic_arg_spans: FxHashMap, nested_elision_site_lts: Vec, unelided_trait_object_lifetime: bool, } @@ -378,6 +406,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), + lifetime_generic_arg_spans: FxHashMap::default(), nested_elision_site_lts: Vec::new(), unelided_trait_object_lifetime: false, } @@ -467,6 +496,22 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { _ => walk_ty(self, ty), } } + + fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { + if let GenericArg::Lifetime(l) = generic_arg + && let LifetimeName::Param(def_id, _) = l.name + { + self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.span); + } + // Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands. + // walk_generic_arg(self, generic_arg); + match generic_arg { + GenericArg::Lifetime(lt) => self.visit_lifetime(lt), + GenericArg::Type(ty) => self.visit_ty(ty), + GenericArg::Const(ct) => self.visit_anon_const(&ct.value), + GenericArg::Infer(inf) => self.visit_infer(inf), + } + } } /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index bcf278d9c833..8e52cac4323c 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -9,7 +9,6 @@ mod manual_flatten; mod manual_memcpy; mod missing_spin_loop; mod mut_range_bound; -mod needless_collect; mod needless_range_loop; mod never_loop; mod same_item_push; @@ -205,28 +204,6 @@ declare_clippy_lint! { "`loop { if let { ... } else break }`, which can be written as a `while let` loop" } -declare_clippy_lint! { - /// ### What it does - /// Checks for functions collecting an iterator when collect - /// is not needed. - /// - /// ### Why is this bad? - /// `collect` causes the allocation of a new data structure, - /// when this allocation may not be needed. - /// - /// ### Example - /// ```rust - /// # let iterator = vec![1].into_iter(); - /// let len = iterator.clone().collect::>().len(); - /// // should be - /// let len = iterator.count(); - /// ``` - #[clippy::version = "1.30.0"] - pub NEEDLESS_COLLECT, - perf, - "collecting an iterator when collect is not needed" -} - declare_clippy_lint! { /// ### What it does /// Checks `for` loops over slices with an explicit counter @@ -605,7 +582,6 @@ declare_lint_pass!(Loops => [ EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, WHILE_LET_LOOP, - NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, EMPTY_LOOP, WHILE_LET_ON_ITERATOR, @@ -667,8 +643,6 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_immutable_condition::check(cx, condition, body); missing_spin_loop::check(cx, condition, body); } - - needless_collect::check(expr, cx); } } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 91b321c44747..4dae93f6028d 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -52,8 +52,8 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option None } -fn check_for_mutation<'tcx>( - cx: &LateContext<'tcx>, +fn check_for_mutation( + cx: &LateContext<'_>, body: &Expr<'_>, bound_id_start: Option, bound_id_end: Option, @@ -113,13 +113,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { } } - fn fake_read( - &mut self, - _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } impl MutatePairDelegate<'_, '_> { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 16b00ad66378..14f161f51026 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; +use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::Span; use std::iter::{once, Iterator}; @@ -16,7 +16,7 @@ pub(super) fn check( span: Span, for_loop: Option<&ForLoop<'_>>, ) { - match never_loop_block(block, loop_id) { + match never_loop_block(block, &mut Vec::new(), loop_id) { NeverLoopResult::AlwaysBreak => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { @@ -92,35 +92,34 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult } } -fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { - let mut iter = block +fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { + let iter = block .stmts .iter() .filter_map(stmt_to_expr) .chain(block.expr.map(|expr| (expr, None))); - never_loop_expr_seq(&mut iter, main_loop_id) -} -fn never_loop_expr_seq<'a, T: Iterator, Option<&'a Block<'a>>)>>( - es: &mut T, - main_loop_id: HirId, -) -> NeverLoopResult { - es.map(|(e, els)| { - let e = never_loop_expr(e, main_loop_id); - els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id))) + iter.map(|(e, els)| { + let e = never_loop_expr(e, ignore_ids, main_loop_id); + // els is an else block in a let...else binding + els.map_or(e, |els| { + combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id)) + }) }) .fold(NeverLoopResult::Otherwise, combine_seq) } fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { match stmt.kind { - StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)), + StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)), + // add the let...else expression (if present) StmtKind::Local(local) => local.init.map(|init| (init, local.els)), StmtKind::Item(..) => None, } } -fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { +#[allow(clippy::too_many_lines)] +fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { match expr.kind { ExprKind::Box(e) | ExprKind::Unary(_, e) @@ -129,47 +128,56 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Repeat(e, _) - | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id), - ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id), - ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), main_loop_id), - ExprKind::MethodCall(_, receiver, es, _) => { - never_loop_expr_all(&mut std::iter::once(receiver).chain(es.iter()), main_loop_id) - }, + | ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id), + ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id), + ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id), + ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all( + &mut std::iter::once(receiver).chain(es.iter()), + ignore_ids, + main_loop_id, + ), ExprKind::Struct(_, fields, base) => { - let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id); + let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id); if let Some(base) = base { - combine_both(fields, never_loop_expr(base, main_loop_id)) + combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id)) } else { fields } }, - ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id), + ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, main_loop_id), ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) - | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id), + | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, main_loop_id), ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. - absorb_break(never_loop_block(b, main_loop_id)) + absorb_break(never_loop_block(b, ignore_ids, main_loop_id)) }, ExprKind::If(e, e2, e3) => { - let e1 = never_loop_expr(e, main_loop_id); - let e2 = never_loop_expr(e2, main_loop_id); - let e3 = e3 - .as_ref() - .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id)); + let e1 = never_loop_expr(e, ignore_ids, main_loop_id); + let e2 = never_loop_expr(e2, ignore_ids, main_loop_id); + let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| { + never_loop_expr(e, ignore_ids, main_loop_id) + }); combine_seq(e1, combine_branches(e2, e3)) }, ExprKind::Match(e, arms, _) => { - let e = never_loop_expr(e, main_loop_id); + let e = never_loop_expr(e, ignore_ids, main_loop_id); if arms.is_empty() { e } else { - let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id); + let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id); combine_seq(e, arms) } }, - ExprKind::Block(b, _) => never_loop_block(b, main_loop_id), + ExprKind::Block(b, l) => { + if l.is_some() { + ignore_ids.push(b.hir_id); + } + let ret = never_loop_block(b, ignore_ids, main_loop_id); + ignore_ids.pop(); + ret + }, ExprKind::Continue(d) => { let id = d .target_id @@ -180,20 +188,32 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { NeverLoopResult::AlwaysBreak } }, + // checks if break targets a block instead of a loop + ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e + .map_or(NeverLoopResult::Otherwise, |e| { + combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise) + }), ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { - combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) + combine_seq( + never_loop_expr(e, ignore_ids, main_loop_id), + NeverLoopResult::AlwaysBreak, + ) }), ExprKind::InlineAsm(asm) => asm .operands .iter() .map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { - never_loop_expr(expr, main_loop_id) + never_loop_expr(expr, ignore_ids, main_loop_id) }, - InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter().copied(), main_loop_id), - InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - never_loop_expr_all(&mut once(*in_expr).chain(out_expr.iter().copied()), main_loop_id) + InlineAsmOperand::Out { expr, .. } => { + never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id) }, + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all( + &mut once(*in_expr).chain(out_expr.iter().copied()), + ignore_ids, + main_loop_id, + ), InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, @@ -208,13 +228,21 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { } } -fn never_loop_expr_all<'a, T: Iterator>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { - es.map(|e| never_loop_expr(e, main_loop_id)) +fn never_loop_expr_all<'a, T: Iterator>>( + es: &mut T, + ignore_ids: &mut Vec, + main_loop_id: HirId, +) -> NeverLoopResult { + es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) .fold(NeverLoopResult::Otherwise, combine_both) } -fn never_loop_expr_branch<'a, T: Iterator>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult { - e.map(|e| never_loop_expr(e, main_loop_id)) +fn never_loop_expr_branch<'a, T: Iterator>>( + e: &mut T, + ignore_ids: &mut Vec, + main_loop_id: HirId, +) -> NeverLoopResult { + e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) .fold(NeverLoopResult::AlwaysBreak, combine_branches) } diff --git a/clippy_lints/src/manual_instant_elapsed.rs b/clippy_lints/src/manual_instant_elapsed.rs deleted file mode 100644 index 331cda1db899..000000000000 --- a/clippy_lints/src/manual_instant_elapsed.rs +++ /dev/null @@ -1,69 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; - -declare_clippy_lint! { - /// ### What it does - /// Lints subtraction between `Instant::now()` and another `Instant`. - /// - /// ### Why is this bad? - /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns - /// as `Instant` subtraction saturates. - /// - /// `prev_instant.elapsed()` also more clearly signals intention. - /// - /// ### Example - /// ```rust - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = Instant::now() - prev_instant; - /// ``` - /// Use instead: - /// ```rust - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = prev_instant.elapsed(); - /// ``` - #[clippy::version = "1.64.0"] - pub MANUAL_INSTANT_ELAPSED, - pedantic, - "subtraction between `Instant::now()` and previous `Instant`" -} - -declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]); - -impl LateLintPass<'_> for ManualInstantElapsed { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind - && check_instant_now_call(cx, lhs) - && let ty_resolved = cx.typeck_results().expr_ty(rhs) - && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind() - && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT) - && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs) - { - span_lint_and_sugg( - cx, - MANUAL_INSTANT_ELAPSED, - expr.span, - "manual implementation of `Instant::elapsed`", - "try", - format!("{}.elapsed()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); - } - } -} - -fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { - if let ExprKind::Call(fn_expr, []) = expr_block.kind - && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) - && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) - { - true - } else { - false - } -} diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs new file mode 100644 index 000000000000..bb8c142f8e46 --- /dev/null +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -0,0 +1,158 @@ +use rustc_ast::LitKind::{Byte, Char}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{def_id::DefId, sym}; + +use clippy_utils::{ + diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet, +}; + +declare_clippy_lint! { + /// ### What it does + /// Suggests to use dedicated built-in methods, + /// `is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range + /// + /// ### Why is this bad? + /// Using the built-in functions is more readable and makes it + /// clear that it's not a specific subset of characters, but all + /// ASCII (lowercase|uppercase|digit) characters. + /// ### Example + /// ```rust + /// fn main() { + /// assert!(matches!('x', 'a'..='z')); + /// assert!(matches!(b'X', b'A'..=b'Z')); + /// assert!(matches!('2', '0'..='9')); + /// assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// assert!('x'.is_ascii_lowercase()); + /// assert!(b'X'.is_ascii_uppercase()); + /// assert!('2'.is_ascii_digit()); + /// assert!('x'.is_ascii_alphabetic()); + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub MANUAL_IS_ASCII_CHECK, + style, + "use dedicated method to check ascii range" +} +impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]); + +pub struct ManualIsAsciiCheck { + msrv: Option, +} + +impl ManualIsAsciiCheck { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +#[derive(Debug, PartialEq)] +enum CharRange { + /// 'a'..='z' | b'a'..=b'z' + LowerChar, + /// 'A'..='Z' | b'A'..=b'Z' + UpperChar, + /// AsciiLower | AsciiUpper + FullChar, + /// '0..=9' + Digit, + Otherwise, +} + +impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) { + return; + } + + if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) { + return; + } + + let Some(macro_call) = root_macro_call(expr.span) else { return }; + + if is_matches_macro(cx, macro_call.def_id) { + if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { + let range = check_pat(&arm.pat.kind); + + if let Some(sugg) = match range { + CharRange::UpperChar => Some("is_ascii_uppercase"), + CharRange::LowerChar => Some("is_ascii_lowercase"), + CharRange::FullChar => Some("is_ascii_alphabetic"), + CharRange::Digit => Some("is_ascii_digit"), + CharRange::Otherwise => None, + } { + let default_snip = ".."; + // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for + // macro span, so we check applicability manually by comparing `recv` is not default. + let recv = snippet(cx, recv.span, default_snip); + + let applicability = if recv == default_snip { + Applicability::HasPlaceholders + } else { + Applicability::MachineApplicable + }; + + span_lint_and_sugg( + cx, + MANUAL_IS_ASCII_CHECK, + macro_call.span, + "manual check for common ascii range", + "try", + format!("{recv}.{sugg}()"), + applicability, + ); + } + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { + match pat_kind { + PatKind::Or(pats) => { + let ranges = pats.iter().map(|p| check_pat(&p.kind)).collect::>(); + + if ranges.len() == 2 && ranges.contains(&CharRange::UpperChar) && ranges.contains(&CharRange::LowerChar) { + CharRange::FullChar + } else { + CharRange::Otherwise + } + }, + PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end), + _ => CharRange::Otherwise, + } +} + +fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { + if let ExprKind::Lit(start_lit) = &start.kind + && let ExprKind::Lit(end_lit) = &end.kind { + match (&start_lit.node, &end_lit.node) { + (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, + (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, + _ => CharRange::Otherwise, + } + } else { + CharRange::Otherwise + } +} + +fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { + if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) { + return sym::matches_macro == name; + } + + false +} diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs new file mode 100644 index 000000000000..1846596fa4c8 --- /dev/null +++ b/clippy_lints/src/manual_let_else.rs @@ -0,0 +1,297 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::{meets_msrv, msrvs, peel_blocks}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::sym; +use rustc_span::Span; +use serde::Deserialize; +use std::ops::ControlFlow; + +declare_clippy_lint! { + /// ### What it does + /// + /// Warn of cases where `let...else` could be used + /// + /// ### Why is this bad? + /// + /// `let...else` provides a standard construct for this pattern + /// that people can easily recognize. It's also more compact. + /// + /// ### Example + /// + /// ```rust + /// # let w = Some(0); + /// let v = if let Some(v) = w { v } else { return }; + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// # #![feature(let_else)] + /// # fn main () { + /// # let w = Some(0); + /// let Some(v) = w else { return }; + /// # } + /// ``` + #[clippy::version = "1.67.0"] + pub MANUAL_LET_ELSE, + pedantic, + "manual implementation of a let...else statement" +} + +pub struct ManualLetElse { + msrv: Option, + matches_behaviour: MatchLintBehaviour, +} + +impl ManualLetElse { + #[must_use] + pub fn new(msrv: Option, matches_behaviour: MatchLintBehaviour) -> Self { + Self { + msrv, + matches_behaviour, + } + } +} + +impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]); + +impl<'tcx> LateLintPass<'tcx> for ManualLetElse { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { + let if_let_or_match = if_chain! { + if meets_msrv(self.msrv, msrvs::LET_ELSE); + if !in_external_macro(cx.sess(), stmt.span); + if let StmtKind::Local(local) = stmt.kind; + if let Some(init) = local.init; + if local.els.is_none(); + if local.ty.is_none(); + if init.span.ctxt() == stmt.span.ctxt(); + if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init); + then { + if_let_or_match + } else { + return; + } + }; + + match if_let_or_match { + IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { + if expr_is_simple_identity(let_pat, if_then); + if let Some(if_else) = if_else; + if expr_diverges(cx, if_else); + then { + emit_manual_let_else(cx, stmt.span, if_let_expr, let_pat, if_else); + } + }, + IfLetOrMatch::Match(match_expr, arms, source) => { + if self.matches_behaviour == MatchLintBehaviour::Never { + return; + } + if source != MatchSource::Normal { + return; + } + // Any other number than two arms doesn't (neccessarily) + // have a trivial mapping to let else. + if arms.len() != 2 { + return; + } + // Guards don't give us an easy mapping either + if arms.iter().any(|arm| arm.guard.is_some()) { + return; + } + let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; + let diverging_arm_opt = arms + .iter() + .enumerate() + .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types)); + let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; + let pat_arm = &arms[1 - idx]; + if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) { + return; + } + + emit_manual_let_else(cx, stmt.span, match_expr, pat_arm.pat, diverging_arm.body); + }, + } + } + + extract_msrv_attr!(LateContext); +} + +fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: &Pat<'_>, else_body: &Expr<'_>) { + span_lint_and_then( + cx, + MANUAL_LET_ELSE, + span, + "this could be rewritten as `let...else`", + |diag| { + // This is far from perfect, for example there needs to be: + // * mut additions for the bindings + // * renamings of the bindings + // * unused binding collision detection with existing ones + // * putting patterns with at the top level | inside () + // for this to be machine applicable. + let app = Applicability::HasPlaceholders; + + if let Some(sn_pat) = snippet_opt(cx, pat.span) && + let Some(sn_expr) = snippet_opt(cx, expr.span) && + let Some(sn_else) = snippet_opt(cx, else_body.span) + { + let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { + sn_else + } else { + format!("{{ {sn_else} }}") + }; + let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};"); + diag.span_suggestion(span, "consider writing", sugg, app); + } + }, + ); +} + +fn expr_diverges(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { + fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { + if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) { + return ty.is_never(); + } + false + } + // We can't just call is_never on expr and be done, because the type system + // sometimes coerces the ! type to something different before we can get + // our hands on it. So instead, we do a manual search. We do fall back to + // is_never in some places when there is no better alternative. + for_each_expr(expr, |ex| { + match ex.kind { + ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()), + ExprKind::Call(call, _) => { + if is_never(cx, ex) || is_never(cx, call) { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::Yes) + }, + ExprKind::MethodCall(..) => { + if is_never(cx, ex) { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::Yes) + }, + ExprKind::If(if_expr, if_then, if_else) => { + let else_diverges = if_else.map_or(false, |ex| expr_diverges(cx, ex)); + let diverges = expr_diverges(cx, if_expr) || (else_diverges && expr_diverges(cx, if_then)); + if diverges { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::No) + }, + ExprKind::Match(match_expr, match_arms, _) => { + let diverges = expr_diverges(cx, match_expr) + || match_arms.iter().all(|arm| { + let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(cx, g.body())); + guard_diverges || expr_diverges(cx, arm.body) + }); + if diverges { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::No) + }, + + // Don't continue into loops or labeled blocks, as they are breakable, + // and we'd have to start checking labels. + ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), + + // Default: descend + _ => ControlFlow::Continue(Descend::Yes), + } + }) + .is_some() +} + +fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool { + // Check whether the pattern contains any bindings, as the + // binding might potentially be used in the body. + // TODO: only look for *used* bindings. + let mut has_bindings = false; + pat.each_binding_or_first(&mut |_, _, _, _| has_bindings = true); + if has_bindings { + return false; + } + + // If we shouldn't check the types, exit early. + if !check_types { + return true; + } + + // Check whether any possibly "unknown" patterns are included, + // because users might not know which values some enum has. + // Well-known enums are excepted, as we assume people know them. + // We do a deep check, to be able to disallow Err(En::Foo(_)) + // for usage of the En::Foo variant, as we disallow En::Foo(_), + // but we allow Err(_). + let typeck_results = cx.typeck_results(); + let mut has_disallowed = false; + pat.walk_always(|pat| { + // Only do the check if the type is "spelled out" in the pattern + if !matches!( + pat.kind, + PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) + ) { + return; + }; + let ty = typeck_results.pat_ty(pat); + // Option and Result are allowed, everything else isn't. + if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) { + has_disallowed = true; + } + }); + !has_disallowed +} + +/// Checks if the passed block is a simple identity referring to bindings created by the pattern +fn expr_is_simple_identity(pat: &'_ Pat<'_>, expr: &'_ Expr<'_>) -> bool { + // We support patterns with multiple bindings and tuples, like: + // let ... = if let (Some(foo), bar) = g() { (foo, bar) } else { ... } + let peeled = peel_blocks(expr); + let paths = match peeled.kind { + ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs, + ExprKind::Path(_) => std::slice::from_ref(peeled), + _ => return false, + }; + let mut pat_bindings = FxHashSet::default(); + pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| { + pat_bindings.insert(ident); + }); + if pat_bindings.len() < paths.len() { + return false; + } + for path in paths { + if_chain! { + if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind; + if let [path_seg] = path.segments; + then { + if !pat_bindings.remove(&path_seg.ident) { + return false; + } + } else { + return false; + } + } + } + true +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize)] +pub enum MatchLintBehaviour { + AllTypes, + WellKnownTypes, + Never, +} diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 32da37a862d8..59195d1ae4e0 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -119,7 +119,7 @@ fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { /// semicolons, which causes problems when generating a suggestion. Given an /// expression that evaluates to '()' or '!', recursively remove useless braces /// and semi-colons until is suitable for including in the suggestion template -fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option { +fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { if !is_unit_expression(cx, expr) { return None; } diff --git a/clippy_lints/src/matches/infallible_destructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs index 2472acb6f6e8..d18c92caba2a 100644 --- a/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath}; +use rustc_hir::{ByRef, ExprKind, Local, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; use super::INFALLIBLE_DESTRUCTURING_MATCH; @@ -16,7 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { if let PatKind::TupleStruct( QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; if args.len() == 1; - if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind; + if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind; let body = peel_blocks(arms[0].body); if path_to_local_id(body, arg); @@ -30,8 +30,9 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { Consider using `let`", "try this", format!( - "let {}({}) = {};", + "let {}({}{}) = {};", snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), + if binding.0 == ByRef::Yes { "ref " } else { "" }, snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), snippet_with_applicability(cx, target.span, "..", &mut applicability), ), diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 66ba1f6f9c55..d521a529e0d6 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -62,7 +62,7 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { // // } // Returns true if resolves to `Some(x)`, `false` otherwise -fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool { +fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { // there can be not statements in the block as they would be removed when switching to `.filter` if let ExprKind::Call(callee, [arg]) = inner_expr.kind { diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 85269e533a06..f587c69f7302 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -83,8 +83,8 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t /// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that /// may have a surprising lifetime. -fn has_significant_drop_in_scrutinee<'tcx, 'a>( - cx: &'a LateContext<'tcx>, +fn has_significant_drop_in_scrutinee<'tcx>( + cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>, source: MatchSource, ) -> Option<(Vec, &'static str)> { @@ -226,7 +226,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { /// This will try to set the current suggestion (so it can be moved into the suggestions vec /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us /// an opportunity to look for another type in the chain that will be trivially copyable. - /// However, if we are at the the end of the chain, we want to accept whatever is there. (The + /// However, if we are at the end of the chain, we want to accept whatever is there. (The /// suggestion won't actually be output, but the diagnostic message will be output, so the user /// can determine the best way to handle the lint.) fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) { @@ -377,7 +377,7 @@ impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> { } } -fn has_significant_drop_in_arms<'tcx, 'a>(cx: &'a LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet { +fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet { let mut helper = ArmSigDropHelper::new(cx); for arm in arms { helper.visit_expr(arm.body); diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index e5a15b2e1a1d..19b49c44d570 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -153,7 +153,7 @@ fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> } /// Returns `true` if the given type is an enum we know won't be expanded in the future -fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { +fn in_candidate_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { // list of candidate `Enum`s we know will never get any more members let candidates = [sym::Cow, sym::Option, sym::Result]; diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index 7e808760663a..27a05337a290 100644 --- a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -9,8 +9,8 @@ use rustc_lint::LateContext; use rustc_lint::Lint; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, +pub(super) fn check( + cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>, chain_methods: &[&str], lint: &'static Lint, diff --git a/clippy_lints/src/methods/chars_last_cmp.rs b/clippy_lints/src/methods/chars_last_cmp.rs index 07bbc5ca1bf4..2efff4c3c549 100644 --- a/clippy_lints/src/methods/chars_last_cmp.rs +++ b/clippy_lints/src/methods/chars_last_cmp.rs @@ -4,7 +4,7 @@ use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { true } else { diff --git a/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs index c29ee0ec8c8c..5b8713f7d790 100644 --- a/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs @@ -4,7 +4,7 @@ use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { true } else { diff --git a/clippy_lints/src/methods/chars_next_cmp.rs b/clippy_lints/src/methods/chars_next_cmp.rs index a6701d8830e7..b631fecab972 100644 --- a/clippy_lints/src/methods/chars_next_cmp.rs +++ b/clippy_lints/src/methods/chars_next_cmp.rs @@ -3,6 +3,6 @@ use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") } diff --git a/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs index 28ede28e9358..caf21d3ff3bc 100644 --- a/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs @@ -3,6 +3,6 @@ use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") } diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs index 501646863fe1..ac61b4377885 100644 --- a/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/clippy_lints/src/methods/collapsible_str_replace.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( // If the parent node's `to` argument is the same as the `to` argument // of the last replace call in the current chain, don't lint as it was already linted if let Some(parent) = get_parent_expr(cx, expr) - && let Some(("replace", _, [current_from, current_to], _)) = method_call(parent) + && let Some(("replace", _, [current_from, current_to], _, _)) = method_call(parent) && eq_expr_value(cx, to, current_to) && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() { @@ -48,7 +48,7 @@ fn collect_replace_calls<'tcx>( let mut from_args = VecDeque::new(); let _: Option<()> = for_each_expr(expr, |e| { - if let Some(("replace", _, [from, to], _)) = method_call(e) { + if let Some(("replace", _, [from, to], _, _)) = method_call(e) { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); from_args.push_front(from); @@ -78,7 +78,7 @@ fn check_consecutive_replace_calls<'tcx>( .collect(); let app = Applicability::MachineApplicable; let earliest_replace_call = replace_methods.methods.front().unwrap(); - if let Some((_, _, [..], span_lo)) = method_call(earliest_replace_call) { + if let Some((_, _, [..], span_lo, _)) = method_call(earliest_replace_call) { span_lint_and_sugg( cx, COLLAPSIBLE_STR_REPLACE, diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index d59fefa1ddc0..cce8f797e98c 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_in_test_function; +use clippy_utils::is_in_cfg_test; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -18,16 +18,16 @@ pub(super) fn check( let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((EXPECT_USED, "an Option", "None", "")) + Some((EXPECT_USED, "an `Option`", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) + Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; let method = if is_err { "expect_err" } else { "expect" }; - if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_expect_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } @@ -36,7 +36,7 @@ pub(super) fn check( cx, lint, expr.span, - &format!("used `{method}()` on `{kind}` value"), + &format!("used `{method}()` on {kind} value"), None, &format!("if this value is {none_prefix}`{none_value}`, it will panic"), ); diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 9719b2f1c512..f888c58a72de 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -17,7 +17,7 @@ use super::MANUAL_FILTER_MAP; use super::MANUAL_FIND_MAP; use super::OPTION_FILTER_MAP; -fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { +fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { match &expr.kind { hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, hir::ExprKind::Path(QPath::Resolved(_, segments)) => { @@ -46,7 +46,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy } } -fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { +fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) } @@ -66,8 +66,8 @@ fn is_filter_some_map_unwrap( /// lint use of `filter().map()` or `find().map()` for `Iterators` #[allow(clippy::too_many_arguments)] -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, +pub(super) fn check( + cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_recv: &hir::Expr<'_>, filter_arg: &hir::Expr<'_>, diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 4f4f543e8a91..d8c821bc9eee 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -12,8 +12,8 @@ use rustc_span::symbol::{Symbol, sym}; use super::INEFFICIENT_TO_STRING; /// Checks for the `INEFFICIENT_TO_STRING` lint -pub fn check<'tcx>( - cx: &LateContext<'tcx>, +pub fn check( + cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, receiver: &hir::Expr<'_>, diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 68d906c3ea39..c830958d5c80 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -10,7 +10,7 @@ use rustc_span::sym; use super::ITER_NTH_ZERO; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if_chain! { if is_trait_method(cx, expr, sym::Iterator); if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg); diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 4f73b3ec4224..70abe4891d98 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -25,7 +25,7 @@ impl IterType { } } -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index b80541b86479..a7284c644977 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -67,7 +67,7 @@ enum MinMax { Max, } -fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option { +fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { // `T::max_value()` `T::min_value()` inherent methods if_chain! { if let hir::ExprKind::Call(func, args) = &expr.kind; diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 13c47c03a80d..a08f7254053f 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -59,10 +59,8 @@ pub(super) fn check( if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String); - if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id); if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id); if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id); if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg); let ctxt = collect_expr.span.ctxt(); diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 7ce14ec080b1..6bc783c6d505 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -15,11 +15,11 @@ use rustc_span::{sym, Span}; use super::MAP_CLONE; -pub(super) fn check<'tcx>( +pub(super) fn check( cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, - arg: &'tcx hir::Expr<'_>, + arg: &hir::Expr<'_>, msrv: Option, ) { if_chain! { diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index d420f144eea1..a0300d278709 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; @@ -11,18 +10,10 @@ use rustc_span::symbol::sym; use super::MAP_COLLECT_RESULT_UNIT; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - iter: &hir::Expr<'_>, - map_fn: &hir::Expr<'_>, - collect_recv: &hir::Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { + // return of collect `Result<(),_>` + let collect_ret_ty = cx.typeck_results().expr_ty(expr); if_chain! { - // called on Iterator - if is_trait_method(cx, collect_recv, sym::Iterator); - // return of collect `Result<(),_>` - let collect_ret_ty = cx.typeck_results().expr_ty(expr); if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result); if let ty::Adt(_, substs) = collect_ret_ty.kind(); if let Some(result_t) = substs.types().next(); diff --git a/clippy_lints/src/methods/map_err_ignore.rs b/clippy_lints/src/methods/map_err_ignore.rs index 1fb6617145e7..b773b3e423f4 100644 --- a/clippy_lints/src/methods/map_err_ignore.rs +++ b/clippy_lints/src/methods/map_err_ignore.rs @@ -6,7 +6,7 @@ use rustc_span::sym; use super::MAP_ERR_IGNORE; -pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8a76ba0b064b..38165ab4fb26 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -54,6 +54,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod mut_mutex_lock; +mod needless_collect; mod needless_option_as_deref; mod needless_option_take; mod no_effect_replace; @@ -69,6 +70,8 @@ mod path_buf_push_overwrite; mod range_zip_with_len; mod repeat_once; mod search_is_some; +mod seek_from_current; +mod seek_to_start_instead_of_rewind; mod single_char_add_str; mod single_char_insert_string; mod single_char_pattern; @@ -101,12 +104,11 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; +use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; +use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -156,9 +158,9 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l"); + /// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l"); /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub COLLAPSIBLE_STR_REPLACE, perf, "collapse consecutive calls to str::replace (2 or more) into a single call" @@ -829,32 +831,30 @@ declare_clippy_lint! { /// etc. instead. /// /// ### Why is this bad? - /// The function will always be called and potentially - /// allocate an object acting as the default. + /// The function will always be called. This is only bad if it allocates or + /// does some non-trivial amount of work. /// /// ### Known problems - /// If the function has side-effects, not calling it will - /// change the semantic of the program, but you shouldn't rely on that anyway. + /// If the function has side-effects, not calling it will change the + /// semantic of the program, but you shouldn't rely on that. + /// + /// The lint also cannot figure out whether the function you call is + /// actually expensive to call or not. /// /// ### Example /// ```rust /// # let foo = Some(String::new()); - /// foo.unwrap_or(String::new()); + /// foo.unwrap_or(String::from("empty")); /// ``` /// /// Use instead: /// ```rust /// # let foo = Some(String::new()); - /// foo.unwrap_or_else(String::new); - /// - /// // or - /// - /// # let foo = Some(String::new()); - /// foo.unwrap_or_default(); + /// foo.unwrap_or_else(|| String::from("empty")); /// ``` #[clippy::version = "pre 1.29.0"] pub OR_FUN_CALL, - perf, + nursery, "using any `*or` method with a function call, which suggests `*or_else`" } @@ -1728,7 +1728,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). + /// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str). /// /// ### Why is this bad? /// Readability, this can be written more concisely as @@ -2094,8 +2094,7 @@ declare_clippy_lint! { /// let s = "Hello world!"; /// let cow = Cow::Borrowed(s); /// - /// let data = cow.into_owned(); - /// assert!(matches!(data, String)) + /// let _data: String = cow.into_owned(); /// ``` #[clippy::version = "1.65.0"] pub SUSPICIOUS_TO_OWNED, @@ -2426,7 +2425,7 @@ declare_clippy_lint! { /// ### Known problems /// /// The type of the resulting iterator might become incompatible with its usage - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub ITER_ON_SINGLE_ITEMS, nursery, "Iterator for array of length 1" @@ -2458,7 +2457,7 @@ declare_clippy_lint! { /// ### Known problems /// /// The type of the resulting iterator might become incompatible with its usage - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub ITER_ON_EMPTY_COLLECTIONS, nursery, "Iterator for empty array" @@ -3066,6 +3065,102 @@ declare_clippy_lint! { "iterating on map using `iter` when `keys` or `values` would do" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks an argument of `seek` method of `Seek` trait + /// and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead. + /// + /// ### Why is this bad? + /// + /// Readability. Use dedicated method. + /// + /// ### Example + /// + /// ```rust,no_run + /// use std::fs::File; + /// use std::io::{self, Write, Seek, SeekFrom}; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello")?; + /// eprintln!("Written {} bytes", f.seek(SeekFrom::Current(0))?); + /// + /// Ok(()) + /// } + /// ``` + /// Use instead: + /// ```rust,no_run + /// use std::fs::File; + /// use std::io::{self, Write, Seek, SeekFrom}; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello")?; + /// eprintln!("Written {} bytes", f.stream_position()?); + /// + /// Ok(()) + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub SEEK_FROM_CURRENT, + complexity, + "use dedicated method for seek from current position" +} + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for jumps to the start of a stream that implements `Seek` + /// and uses the `seek` method providing `Start` as parameter. + /// + /// ### Why is this bad? + /// + /// Readability. There is a specific method that was implemented for + /// this exact scenario. + /// + /// ### Example + /// ```rust + /// # use std::io; + /// fn foo(t: &mut T) { + /// t.seek(io::SeekFrom::Start(0)); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::io; + /// fn foo(t: &mut T) { + /// t.rewind(); + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub SEEK_TO_START_INSTEAD_OF_REWIND, + complexity, + "jumping to the start of stream using `seek` method" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for functions collecting an iterator when collect + /// is not needed. + /// + /// ### Why is this bad? + /// `collect` causes the allocation of a new data structure, + /// when this allocation may not be needed. + /// + /// ### Example + /// ```rust + /// # let iterator = vec![1].into_iter(); + /// let len = iterator.clone().collect::>().len(); + /// // should be + /// let len = iterator.count(); + /// ``` + #[clippy::version = "1.30.0"] + pub NEEDLESS_COLLECT, + nursery, + "collecting an iterator when collect is not needed" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -3190,16 +3285,19 @@ impl_lint_pass!(Methods => [ VEC_RESIZE_TO_ZERO, VERBOSE_FILE_READS, ITER_KV_MAP, + SEEK_FROM_CURRENT, + SEEK_TO_START_INSTEAD_OF_REWIND, + NEEDLESS_COLLECT, ]); /// Extracts a method call name, args, and `Span` of the method name. fn method_call<'tcx>( recv: &'tcx hir::Expr<'tcx>, -) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span)> { - if let ExprKind::MethodCall(path, receiver, args, _) = recv.kind { +) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span, Span)> { + if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind { if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { let name = path.ident.name.as_str(); - return Some((name, receiver, args, path.ident.span)); + return Some((name, receiver, args, path.ident.span, call_span)); } } None @@ -3316,36 +3414,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id()); - // walk the return type and check for Self (this does not check associated types) - if let Some(self_adt) = self_ty.ty_adt_def() { - if contains_adt_constructor(ret_ty, self_adt) { - return; - } - } else if ret_ty.contains(self_ty) { + if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) { return; } - // if return type is impl trait, check the associated types - if let ty::Opaque(def_id, _) = *ret_ty.kind() { - // one of the associated types must be Self - for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { - if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { - let assoc_ty = match projection_predicate.term.unpack() { - ty::TermKind::Ty(ty) => ty, - ty::TermKind::Const(_c) => continue, - }; - // walk the associated type and check for Self - if let Some(self_adt) = self_ty.ty_adt_def() { - if contains_adt_constructor(assoc_ty, self_adt) { - return; - } - } else if assoc_ty.contains(self_ty) { - return; - } - } - } - } - if name == "new" && ret_ty != self_ty { span_lint( cx, @@ -3411,7 +3483,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { impl Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((name, recv, args, span)) = method_call(expr) { + if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); @@ -3430,28 +3502,31 @@ impl Methods { ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv), - ("collect", []) => match method_call(recv) { - Some((name @ ("cloned" | "copied"), recv2, [], _)) => { - iter_cloned_collect::check(cx, name, expr, recv2); - }, - Some(("map", m_recv, [m_arg], _)) => { - map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); - }, - Some(("take", take_self_arg, [take_arg], _)) => { - if meets_msrv(self.msrv, msrvs::STR_REPEAT) { - manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); - } - }, - _ => {}, + ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { + needless_collect::check(cx, span, expr, recv, call_span); + match method_call(recv) { + Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => { + iter_cloned_collect::check(cx, name, expr, recv2); + }, + Some(("map", m_recv, [m_arg], _, _)) => { + map_collect_result_unit::check(cx, expr, m_recv, m_arg); + }, + Some(("take", take_self_arg, [take_arg], _, _)) => { + if meets_msrv(self.msrv, msrvs::STR_REPEAT) { + manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + } + }, + _ => {}, + } }, ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _)) => { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), + Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => { iter_count::check(cx, expr, recv2, name2); }, - Some(("map", _, [arg], _)) => suspicious_map::check(cx, expr, recv, arg), - Some(("filter", recv2, [arg], _)) => bytecount::check(cx, expr, recv2, arg), - Some(("bytes", recv2, [], _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + Some(("map", _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some(("filter", recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), _ => {}, }, ("drain", [arg]) => { @@ -3463,8 +3538,8 @@ impl Methods { } }, ("expect", [_]) => match method_call(recv) { - Some(("ok", recv, [], _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), + Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), + Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), }, ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests), @@ -3484,13 +3559,13 @@ impl Methods { flat_map_option::check(cx, expr, arg, span); }, ("flatten", []) => match method_call(recv) { - Some(("map", recv, [map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), + Some(("map", recv, [map_arg], map_span, _)) => map_flatten::check(cx, expr, recv, map_arg, map_span), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), _ => {}, }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("for_each", [_]) => { - if let Some(("inspect", _, [_], span2)) = method_call(recv) { + if let Some(("inspect", _, [_], span2, _)) = method_call(recv) { inspect_for_each::check(cx, expr, span2); } }, @@ -3510,12 +3585,12 @@ impl Methods { iter_on_single_or_empty_collections::check(cx, expr, name, recv); }, ("join", [join_arg]) => { - if let Some(("collect", _, _, span)) = method_call(recv) { + if let Some(("collect", _, _, span, _)) = method_call(recv) { unnecessary_join::check(cx, expr, recv, join_arg, span); } }, ("last", []) | ("skip", [_]) => { - if let Some((name2, recv2, args2, _span2)) = method_call(recv) { + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } @@ -3527,13 +3602,13 @@ impl Methods { (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { map_clone::check(cx, expr, recv, m_arg, self.msrv); - if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _)) = method_call(recv) { + if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) { iter_kv_map::check(cx, map_name, expr, recv2, m_arg); } } else { map_err_ignore::check(cx, expr, m_arg); } - if let Some((name, recv2, args, span2)) = method_call(recv) { + if let Some((name, recv2, args, span2,_)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), @@ -3553,7 +3628,7 @@ impl Methods { manual_ok_or::check(cx, expr, recv, def, map); }, ("next", []) => { - if let Some((name2, recv2, args2, _)) = method_call(recv) { + if let Some((name2, recv2, args2, _, _)) = method_call(recv) { match (name2, args2) { ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), @@ -3566,10 +3641,10 @@ impl Methods { } }, ("nth", [n_arg]) => match method_call(recv) { - Some(("bytes", recv2, [], _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), - Some(("iter", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), - Some(("iter_mut", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), + Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), + Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), @@ -3604,6 +3679,14 @@ impl Methods { ("resize", [count_arg, default_arg]) => { vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); }, + ("seek", [arg]) => { + if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) { + seek_from_current::check(cx, expr, recv, arg); + } + if meets_msrv(self.msrv, msrvs::SEEK_REWIND) { + seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span); + } + }, ("sort", []) => { stable_sort_primitive::check(cx, expr, recv); }, @@ -3626,7 +3709,7 @@ impl Methods { }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("take", [_arg]) => { - if let Some((name2, recv2, args2, _span2)) = method_call(recv) { + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } @@ -3649,13 +3732,13 @@ impl Methods { }, ("unwrap", []) => { match method_call(recv) { - Some(("get", recv, [get_arg], _)) => { + Some(("get", recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, false); }, - Some(("get_mut", recv, [get_arg], _)) => { + Some(("get_mut", recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, true); }, - Some(("or", recv, [or_arg], or_span)) => { + Some(("or", recv, [or_arg], or_span, _)) => { or_then_unwrap::check(cx, expr, recv, or_arg, or_span); }, _ => {}, @@ -3664,19 +3747,19 @@ impl Methods { }, ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests), ("unwrap_or", [u_arg]) => match method_call(recv) { - Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _)) => { + Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => { manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, - Some(("map", m_recv, [m_arg], span)) => { + Some(("map", m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); }, - Some(("then_some", t_recv, [t_arg], _)) => { + Some(("then_some", t_recv, [t_arg], _, _)) => { obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg); }, _ => {}, }, ("unwrap_or_else", [u_arg]) => match method_call(recv) { - Some(("map", recv, [map_arg], _)) + Some(("map", recv, [map_arg], _, _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); @@ -3697,7 +3780,7 @@ impl Methods { } fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { - if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span)) = method_call(recv) { + if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span, _)) = method_call(recv) { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); } } @@ -3906,14 +3989,6 @@ impl OutType { } } -fn is_bool(ty: &hir::Ty<'_>) -> bool { - if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - matches!(path.res, Res::PrimTy(PrimTy::Bool)) - } else { - false - } -} - fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { expected.constness == actual.constness && expected.unsafety == actual.unsafety diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs similarity index 62% rename from clippy_lints/src/loops/needless_collect.rs rename to clippy_lints/src/methods/needless_collect.rs index 66f9e28596e8..b088e642e0e9 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -3,94 +3,99 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind}; -use if_chain::if_chain; +use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection}; +use clippy_utils::{ + can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id, + CaptureKind, +}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind}; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind, +}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; -use rustc_span::Span; +use rustc_middle::ty::{self, AssocKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, Span, Symbol}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; -pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - check_needless_collect_direct_usage(expr, cx); - check_needless_collect_indirect_usage(expr, cx); -} -fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if_chain! { - if let ExprKind::MethodCall(method, receiver, args, _) = expr.kind; - if let ExprKind::MethodCall(chain_method, ..) = receiver.kind; - if chain_method.ident.name == sym!(collect) && is_trait_method(cx, receiver, sym::Iterator); - then { - let ty = cx.typeck_results().expr_ty(receiver); - let mut applicability = Applicability::MaybeIncorrect; - let is_empty_sugg = "next().is_none()".to_string(); - let method_name = method.ident.name.as_str(); - let sugg = if is_type_diagnostic_item(cx, ty, sym::Vec) || - is_type_diagnostic_item(cx, ty, sym::VecDeque) || - is_type_diagnostic_item(cx, ty, sym::LinkedList) || - is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { - match method_name { - "len" => "count()".to_string(), - "is_empty" => is_empty_sugg, - "contains" => { - let contains_arg = snippet_with_applicability(cx, args[0].span, "??", &mut applicability); - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - format!("any(|{arg}| x == {pred})") - } - _ => return, - } - } - else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) || - is_type_diagnostic_item(cx, ty, sym::HashMap) { - match method_name { - "is_empty" => is_empty_sugg, - _ => return, - } - } - else { - return; - }; - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - chain_method.ident.span.with_hi(expr.span.hi()), - NEEDLESS_COLLECT_MSG, - "replace with", - sugg, - applicability, - ); - } - } -} +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + name_span: Span, + collect_expr: &'tcx Expr<'_>, + iter_expr: &'tcx Expr<'tcx>, + call_span: Span, +) { + if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) { + match parent { + Node::Expr(parent) => { + if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind { + let mut app = Applicability::MachineApplicable; + let name = name.ident.as_str(); + let collect_ty = cx.typeck_results().expr_ty(collect_expr); -fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if let ExprKind::Block(block, _) = expr.kind { - for stmt in block.stmts { - if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(_, id, ..) = local.pat.kind; - if let Some(init_expr) = local.init; - if let ExprKind::MethodCall(method_name, iter_source, [], ..) = init_expr.kind; - if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); - let ty = cx.typeck_results().expr_ty(init_expr); - if is_type_diagnostic_item(cx, ty, sym::Vec) || - is_type_diagnostic_item(cx, ty, sym::VecDeque) || - is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || - is_type_diagnostic_item(cx, ty, sym::LinkedList); - let iter_ty = cx.typeck_results().expr_ty(iter_source); - if let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)); - if let [iter_call] = &*iter_calls; - then { + let sugg: String = match name { + "len" => { + if let Some(adt) = collect_ty.ty_adt_def() + && matches!( + cx.tcx.get_diagnostic_name(adt.did()), + Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap) + ) + { + "count()".into() + } else { + return; + } + }, + "is_empty" + if is_is_empty_sig(cx, parent.hir_id) + && iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) => + { + "next().is_none()".into() + }, + "contains" => { + if is_contains_sig(cx, parent.hir_id, iter_expr) + && let Some(arg) = args.first() + { + let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind { + (arg.span, "") + } else { + (arg.span, "*") + }; + let snip = snippet_with_applicability(cx, span, "??", &mut app); + format!("any(|x| x == {prefix}{snip})") + } else { + return; + } + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + call_span.with_hi(parent.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + app, + ); + } + }, + Node::Local(l) => { + if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) + = l.pat.kind + && let ty = cx.typeck_results().expr_ty(collect_expr) + && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList].into_iter() + .any(|item| is_type_diagnostic_item(cx, ty, item)) + && let iter_ty = cx.typeck_results().expr_ty(iter_expr) + && let Some(block) = get_enclosing_block(cx, l.hir_id) + && let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)) + && let [iter_call] = &*iter_calls + { let mut used_count_visitor = UsedCountVisitor { cx, id, @@ -102,20 +107,20 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo } // Suggest replacing iter_call with iter_replacement, and removing stmt - let mut span = MultiSpan::from_span(method_name.ident.span); + let mut span = MultiSpan::from_span(name_span); span.push_span_label(iter_call.span, "the iterator could be used here instead"); span_lint_hir_and_then( cx, super::NEEDLESS_COLLECT, - init_expr.hir_id, + collect_expr.hir_id, span, NEEDLESS_COLLECT_MSG, |diag| { - let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); + let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx)); diag.multipart_suggestion( iter_call.get_suggestion_text(), vec![ - (stmt.span, String::new()), + (l.span, String::new()), (iter_call.span, iter_replacement) ], Applicability::MaybeIncorrect, @@ -123,11 +128,61 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo }, ); } - } + }, + _ => (), } } } +/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool` +fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { + cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| { + let sig = cx.tcx.fn_sig(id).skip_binder(); + sig.inputs().len() == 1 && sig.output().is_bool() + }) +} + +/// Checks if `::Item` is the same as `::Item` +fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool { + let item = Symbol::intern("Item"); + if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator) + && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, item, [iter_ty]) + && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty]) + && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( + cx.param_env, + cx.tcx.mk_projection(into_iter_item_proj.item_def_id, into_iter_item_proj.substs) + ) + { + iter_item_ty == into_iter_item_ty + } else { + false + } +} + +/// Checks if the given method call matches the expected signature of +/// `([&[mut]] self, &::Item) -> bool` +fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -> bool { + let typeck = cx.typeck_results(); + if let Some(id) = typeck.type_dependent_def_id(call_id) + && let sig = cx.tcx.fn_sig(id) + && sig.skip_binder().output().is_bool() + && let [_, search_ty] = *sig.skip_binder().inputs() + && let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind() + && let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(iter_item) = cx.tcx + .associated_items(iter_trait) + .find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait) + && let substs = cx.tcx.mk_substs([GenericArg::from(typeck.expr_ty_adjusted(iter_expr))].into_iter()) + && let proj_ty = cx.tcx.mk_projection(iter_item.def_id, substs) + && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty) + { + item_ty == EarlyBinder(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id)) + } else { + false + } +} + struct IterFunction { func: IterFunctionKind, span: Span, diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 742483e6b2e5..e6eb64bcbde6 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -13,8 +13,8 @@ use rustc_span::sym; use super::OPTION_AS_REF_DEREF; /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, +pub(super) fn check( + cx: &LateContext<'_>, expr: &hir::Expr<'_>, as_ref_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 991d3dd538bf..4460f38fcc18 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -83,6 +83,8 @@ pub(super) fn check<'tcx>( method_span: Span, self_expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, + // `Some` if fn has second argument + second_arg: Option<&hir::Expr<'_>>, span: Span, // None if lambda is required fun_span: Option, @@ -109,30 +111,40 @@ pub(super) fn check<'tcx>( if poss.contains(&name); then { - let macro_expanded_snipped; - let sugg: Cow<'_, str> = { + let sugg = { let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { (false, Some(fun_span)) => (fun_span, false), _ => (arg.span, true), }; - let snippet = { - let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); - if not_macro_argument_snippet == "vec![]" { - macro_expanded_snipped = snippet(cx, snippet_span, ".."); + + let format_span = |span: Span| { + let not_macro_argument_snippet = snippet_with_macro_callsite(cx, span, ".."); + let snip = if not_macro_argument_snippet == "vec![]" { + let macro_expanded_snipped = snippet(cx, snippet_span, ".."); match macro_expanded_snipped.strip_prefix("$crate::vec::") { - Some(stripped) => Cow::from(stripped), + Some(stripped) => Cow::Owned(stripped.to_owned()), None => macro_expanded_snipped, } } else { not_macro_argument_snippet - } + }; + + snip.to_string() }; - if use_lambda { + let snip = format_span(snippet_span); + let snip = if use_lambda { let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{l_arg}| {snippet}").into() + format!("|{l_arg}| {snip}") } else { - snippet + snip + }; + + if let Some(f) = second_arg { + let f = format_span(f.span); + format!("{snip}, {f}") + } else { + snip } }; let span_replace_word = method_span.with_hi(span.hi()); @@ -149,8 +161,8 @@ pub(super) fn check<'tcx>( } } - if let [arg] = args { - let inner_arg = if let hir::ExprKind::Block( + let extract_inner_arg = |arg: &'tcx hir::Expr<'_>| { + if let hir::ExprKind::Block( hir::Block { stmts: [], expr: Some(expr), @@ -162,19 +174,32 @@ pub(super) fn check<'tcx>( expr } else { arg - }; + } + }; + + if let [arg] = args { + let inner_arg = extract_inner_arg(arg); match inner_arg.kind { hir::ExprKind::Call(fun, or_args) => { let or_has_args = !or_args.is_empty(); if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) { let fun_span = if or_has_args { None } else { Some(fun.span) }; - check_general_case(cx, name, method_span, receiver, arg, expr.span, fun_span); + check_general_case(cx, name, method_span, receiver, arg, None, expr.span, fun_span); } }, hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { - check_general_case(cx, name, method_span, receiver, arg, expr.span, None); + check_general_case(cx, name, method_span, receiver, arg, None, expr.span, None); }, _ => (), } } + + // `map_or` takes two arguments + if let [arg, lambda] = args { + let inner_arg = extract_inner_arg(arg); + if let hir::ExprKind::Call(fun, or_args) = inner_arg.kind { + let fun_span = if or_args.is_empty() { Some(fun.span) } else { None }; + check_general_case(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span); + } + } } diff --git a/clippy_lints/src/methods/seek_from_current.rs b/clippy_lints/src/methods/seek_from_current.rs new file mode 100644 index 000000000000..361a3082f949 --- /dev/null +++ b/clippy_lints/src/methods/seek_from_current.rs @@ -0,0 +1,48 @@ +use rustc_ast::ast::{LitIntType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; + +use clippy_utils::{ + diagnostics::span_lint_and_sugg, get_trait_def_id, match_def_path, paths, source::snippet_with_applicability, + ty::implements_trait, +}; + +use super::SEEK_FROM_CURRENT; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv); + + if let Some(def_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) { + if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) { + let mut applicability = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability); + + span_lint_and_sugg( + cx, + SEEK_FROM_CURRENT, + expr.span, + "using `SeekFrom::Current` to start from current position", + "replace with", + format!("{snip}.stream_position()"), + applicability, + ); + } + } +} + +fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + if let ExprKind::Call(f, args) = expr.kind && + let ExprKind::Path(ref path) = f.kind && + let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id() && + match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) { + // check if argument of `SeekFrom::Current` is `0` + if args.len() == 1 && + let ExprKind::Lit(ref lit) = args[0].kind && + let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node { + return true + } + } + + false +} diff --git a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs new file mode 100644 index 000000000000..7e3bed1e41a9 --- /dev/null +++ b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -0,0 +1,45 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, match_def_path, paths}; +use rustc_ast::ast::{LitIntType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::SEEK_TO_START_INSTEAD_OF_REWIND; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + recv: &'tcx Expr<'_>, + arg: &'tcx Expr<'_>, + name_span: Span, +) { + // Get receiver type + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + + if let Some(seek_trait_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) && + implements_trait(cx, ty, seek_trait_id, &[]) && + let ExprKind::Call(func, args1) = arg.kind && + let ExprKind::Path(ref path) = func.kind && + let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() && + match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START) && + args1.len() == 1 && + let ExprKind::Lit(ref lit) = args1[0].kind && + let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + { + let method_call_span = expr.span.with_lo(name_span.lo()); + span_lint_and_then( + cx, + SEEK_TO_START_INSTEAD_OF_REWIND, + method_call_span, + "used `seek` to go to the start of the stream", + |diag| { + let app = Applicability::MachineApplicable; + + diag.span_suggestion(method_call_span, "replace with", "rewind()", app); + }, + ); + } +} diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 6f4cec546e96..f35d81cee8e9 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -18,7 +18,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr let target = &arglists[0].0; let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if *self_ty.kind() == ty::Str { - "" + if matches!(target.kind, hir::ExprKind::Index(..)) { + "&" + } else { + "" + } } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) { "&" } else { diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 851cdf544550..2ac0786b37b1 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -8,7 +8,7 @@ use rustc_span::sym; use super::SUSPICIOUS_MAP; -pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { if_chain! { if is_trait_method(cx, count_recv, sym::Iterator); let closure = expr_or_init(cx, map_arg); diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index c9b87bc6bf29..087e1e4343b7 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>( cx, UNNECESSARY_JOIN, span.with_hi(expr.span.hi()), - r#"called `.collect>().join("")` on an iterator"#, + r#"called `.collect::>().join("")` on an iterator"#, "try using", "collect::()".to_owned(), applicability, diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index ee17f2d7889e..90983f249cd5 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_in_test_function, is_lint_allowed}; +use clippy_utils::{is_in_cfg_test, is_lint_allowed}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -18,16 +18,16 @@ pub(super) fn check( let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((UNWRAP_USED, "an Option", "None", "")) + Some((UNWRAP_USED, "an `Option`", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) + Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; let method_suffix = if is_err { "_err" } else { "" }; - if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_unwrap_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } @@ -45,7 +45,7 @@ pub(super) fn check( cx, lint, expr.span, - &format!("used `unwrap{method_suffix}()` on `{kind}` value"), + &format!("used `unwrap{method_suffix}()` on {kind} value"), None, &help, ); diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 872679f25ab5..4712846939e6 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -59,7 +59,7 @@ impl LateLintPass<'_> for ImportRename { fn check_crate(&mut self, cx: &LateContext<'_>) { for Rename { path, rename } in &self.conf_renames { let segs = path.split("::").collect::>(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, None) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.renames.insert(id, Symbol::intern(rename)); } } diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 6752976348f6..321fa4b7f999 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -218,7 +218,7 @@ enum StopEarly { Stop, } -fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly { +fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly { if expr.hir_id == vis.last_expr.hir_id { return StopEarly::KeepGoing; } @@ -265,7 +265,7 @@ fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) - StopEarly::KeepGoing } -fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { +fn check_stmt<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr), // If the declaration is of a local variable, check its initializer diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 4b62dcdffe2f..a651020ca656 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::trait_ref_of_method; +use clippy_utils::{def_path_def_ids, trait_ref_of_method}; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TypeVisitable; use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; use std::iter; @@ -78,26 +79,44 @@ declare_clippy_lint! { "Check for mutable `Map`/`Set` key type" } -declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); +#[derive(Clone)] +pub struct MutableKeyType { + ignore_interior_mutability: Vec, + ignore_mut_def_ids: FxHashSet, +} + +impl_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); impl<'tcx> LateLintPass<'tcx> for MutableKeyType { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + self.ignore_mut_def_ids.clear(); + let mut path = Vec::new(); + for ty in &self.ignore_interior_mutability { + path.extend(ty.split("::")); + for id in def_path_def_ids(cx, &path[..]) { + self.ignore_mut_def_ids.insert(id); + } + path.clear(); + } + } + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { if let hir::ItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), sig.decl); + self.check_sig(cx, item.hir_id(), sig.decl); } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { if trait_ref_of_method(cx, item.owner_id.def_id).is_none() { - check_sig(cx, item.hir_id(), sig.decl); + self.check_sig(cx, item.hir_id(), sig.decl); } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), sig.decl); + self.check_sig(cx, item.hir_id(), sig.decl); } } @@ -105,73 +124,81 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { if let hir::PatKind::Wild = local.pat.kind { return; } - check_ty(cx, local.span, cx.typeck_results().pat_ty(local.pat)); + self.check_ty_(cx, local.span, cx.typeck_results().pat_ty(local.pat)); } } -fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) { - let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id); - let fn_sig = cx.tcx.fn_sig(fn_def_id); - for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { - check_ty(cx, hir_ty.span, *ty); +impl MutableKeyType { + pub fn new(ignore_interior_mutability: Vec) -> Self { + Self { + ignore_interior_mutability, + ignore_mut_def_ids: FxHashSet::default(), + } } - check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output())); -} -// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased -// generics (because the compiler cannot ensure immutability for unknown types). -fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { - let ty = ty.peel_refs(); - if let Adt(def, substs) = ty.kind() { - let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] - .iter() - .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); - if is_keyed_type && is_interior_mutable_type(cx, substs.type_at(0), span) { - span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); + fn check_sig(&self, cx: &LateContext<'_>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) { + let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id); + let fn_sig = cx.tcx.fn_sig(fn_def_id); + for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { + self.check_ty_(cx, hir_ty.span, *ty); } + self.check_ty_(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output())); } -} -/// Determines if a type contains interior mutability which would affect its implementation of -/// [`Hash`] or [`Ord`]. -fn is_interior_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool { - match *ty.kind() { - Ref(_, inner_ty, mutbl) => { - mutbl == hir::Mutability::Mut || is_interior_mutable_type(cx, inner_ty, span) - } - Slice(inner_ty) => is_interior_mutable_type(cx, inner_ty, span), - Array(inner_ty, size) => { - size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) - && is_interior_mutable_type(cx, inner_ty, span) - } - Tuple(fields) => fields.iter().any(|ty| is_interior_mutable_type(cx, ty, span)), - Adt(def, substs) => { - // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to - // that of their type parameters. Note: we don't include `HashSet` and `HashMap` - // because they have no impl for `Hash` or `Ord`. - let is_std_collection = [ - sym::Option, - sym::Result, - sym::LinkedList, - sym::Vec, - sym::VecDeque, - sym::BTreeMap, - sym::BTreeSet, - sym::Rc, - sym::Arc, - ] - .iter() - .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); - let is_box = Some(def.did()) == cx.tcx.lang_items().owned_box(); - if is_std_collection || is_box { - // The type is mutable if any of its type parameters are - substs.types().any(|ty| is_interior_mutable_type(cx, ty, span)) - } else { - !ty.has_escaping_bound_vars() - && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() - && !ty.is_freeze(cx.tcx, cx.param_env) + // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased + // generics (because the compiler cannot ensure immutability for unknown types). + fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { + let ty = ty.peel_refs(); + if let Adt(def, substs) = ty.kind() { + let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); + if is_keyed_type && self.is_interior_mutable_type(cx, substs.type_at(0)) { + span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); } } - _ => false, + } + + /// Determines if a type contains interior mutability which would affect its implementation of + /// [`Hash`] or [`Ord`]. + fn is_interior_mutable_type<'tcx>(&self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match *ty.kind() { + Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || self.is_interior_mutable_type(cx, inner_ty), + Slice(inner_ty) => self.is_interior_mutable_type(cx, inner_ty), + Array(inner_ty, size) => { + size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) + && self.is_interior_mutable_type(cx, inner_ty) + }, + Tuple(fields) => fields.iter().any(|ty| self.is_interior_mutable_type(cx, ty)), + Adt(def, substs) => { + // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to + // that of their type parameters. Note: we don't include `HashSet` and `HashMap` + // because they have no impl for `Hash` or `Ord`. + let def_id = def.did(); + let is_std_collection = [ + sym::Option, + sym::Result, + sym::LinkedList, + sym::Vec, + sym::VecDeque, + sym::BTreeMap, + sym::BTreeSet, + sym::Rc, + sym::Arc, + ] + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id)); + let is_box = Some(def_id) == cx.tcx.lang_items().owned_box(); + if is_std_collection || is_box || self.ignore_mut_def_ids.contains(&def_id) { + // The type is mutable if any of its type parameters are + substs.types().any(|ty| self.is_interior_mutable_type(cx, ty)) + } else { + !ty.has_escaping_bound_vars() + && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() + && !ty.is_freeze(cx.tcx, cx.param_env) + } + }, + _ => false, + } } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index cb16f00047a3..bc90e131b7f3 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -68,13 +68,15 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { expr.span, "generally you want to avoid `&mut &mut _` if possible", ); - } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { - span_lint( - self.cx, - MUT_MUT, - expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", - ); + } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { + if ty.peel_refs().is_sized(self.cx.tcx, self.cx.param_env) { + span_lint( + self.cx, + MUT_MUT, + expr.span, + "this expression mutably borrows a mutable reference. Consider reborrowing", + ); + } } } } diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 10c3ff026b6d..498e1408e52a 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -36,14 +36,14 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { - fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - if pat.span.from_expansion() { + fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) { + if ref_pat.span.from_expansion() { // OK, simple enough, lints doesn't check in macro. return; } // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms - for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) { + for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) { let Node::Pat(pat) = node else { break }; if matches!(pat.kind, PatKind::Or(_)) { @@ -52,20 +52,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { } // Only lint immutable refs, because `&mut ref T` may be useful. - let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return }; + let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { return }; - match sub_pat.kind { + match pat.kind { // Check sub_pat got a `ref` keyword (excluding `ref mut`). PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { span_lint_and_then( cx, NEEDLESS_BORROWED_REFERENCE, - pat.span, + ref_pat.span, "this pattern takes a reference on something that is being dereferenced", |diag| { // `&ref ident` // ^^^^^ - let span = pat.span.until(ident.span); + let span = ref_pat.span.until(ident.span); diag.span_suggestion_verbose( span, "try removing the `&ref` part", @@ -84,41 +84,71 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { }), after, ) => { - let mut suggestions = Vec::new(); - - for element_pat in itertools::chain(before, after) { - if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind { - // `&[..., ref ident, ...]` - // ^^^^ - let span = element_pat.span.until(ident.span); - suggestions.push((span, String::new())); - } else { - return; - } - } + check_subpatterns( + cx, + "dereferencing a slice pattern where every element takes a reference", + ref_pat, + pat, + itertools::chain(before, after), + ); + }, + PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { + check_subpatterns( + cx, + "dereferencing a tuple pattern where every element takes a reference", + ref_pat, + pat, + subpatterns, + ); + }, + PatKind::Struct(_, fields, _) => { + check_subpatterns( + cx, + "dereferencing a struct pattern where every field's pattern takes a reference", + ref_pat, + pat, + fields.iter().map(|field| field.pat), + ); + }, + _ => {}, + } + } +} - if !suggestions.is_empty() { - span_lint_and_then( - cx, - NEEDLESS_BORROWED_REFERENCE, - pat.span, - "dereferencing a slice pattern where every element takes a reference", - |diag| { - // `&[...]` - // ^ - let span = pat.span.until(sub_pat.span); - suggestions.push((span, String::new())); +fn check_subpatterns<'tcx>( + cx: &LateContext<'tcx>, + message: &str, + ref_pat: &Pat<'_>, + pat: &Pat<'_>, + subpatterns: impl IntoIterator>, +) { + let mut suggestions = Vec::new(); - diag.multipart_suggestion( - "try removing the `&` and `ref` parts", - suggestions, - Applicability::MachineApplicable, - ); - }, - ); - } + for subpattern in subpatterns { + match subpattern.kind { + PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { + // `ref ident` + // ^^^^ + let span = subpattern.span.until(ident.span); + suggestions.push((span, String::new())); }, - _ => {}, + PatKind::Wild => {}, + _ => return, } } + + if !suggestions.is_empty() { + span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, ref_pat.span, message, |diag| { + // `&pat` + // ^ + let span = ref_pat.span.until(pat.span); + suggestions.push((span, String::new())); + + diag.multipart_suggestion( + "try removing the `&` and `ref` parts", + suggestions, + Applicability::MachineApplicable, + ); + }); + } } diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 6f0e755466e5..38a75034cd31 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -287,7 +287,7 @@ const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause"; const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression"; -fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) { +fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { // snip is the whole *help* message that appears after the warning. // message is the warning message. // expr is the expression which the lint warning message refers to. @@ -313,7 +313,7 @@ fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, ); } -fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String { +fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { let cond_code = snippet(cx, data.if_cond.span, ".."); let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)); @@ -327,7 +327,7 @@ fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &' ) } -fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String { +fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { let cond_code = snippet(cx, data.if_cond.span, ".."); // Region B @@ -361,7 +361,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: ) } -fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) { +fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { if_chain! { if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind; if let Some(last_stmt) = loop_block.stmts.last(); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 79aa15b06ef4..2ef902965f66 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -340,11 +340,5 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read( - &mut self, - _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index 2a7159764e46..ae0a41db918a 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -58,9 +58,9 @@ impl EarlyLintPass for OctalEscapes { if let ExprKind::Lit(token_lit) = &expr.kind { if matches!(token_lit.kind, LitKind::Str) { - check_lit(cx, &token_lit, expr.span, true); + check_lit(cx, token_lit, expr.span, true); } else if matches!(token_lit.kind, LitKind::ByteStr) { - check_lit(cx, &token_lit, expr.span, false); + check_lit(cx, token_lit, expr.span, false); } } } diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 8827daaa3ee7..20b82d81a2ae 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,5 +1,9 @@ use super::ARITHMETIC_SIDE_EFFECTS; -use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; +use clippy_utils::{ + consts::{constant, constant_simple}, + diagnostics::span_lint, + peel_hir_expr_refs, +}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; @@ -38,24 +42,6 @@ impl ArithmeticSideEffects { } } - /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a - /// non-constant environment that won't overflow. - fn has_valid_op(op: &Spanned, expr: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::Lit(ref lit) = expr.kind && - let ast::LitKind::Int(value, _) = lit.node - { - match (&op.node, value) { - (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, - (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) - | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) - | (hir::BinOpKind::Mul, 0 | 1) => true, - _ => false, - } - } else { - false - } - } - /// Checks if the given `expr` has any of the inner `allowed` elements. fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { self.allowed @@ -74,15 +60,14 @@ impl ArithmeticSideEffects { self.expr_span = Some(expr.span); } - /// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`. - fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option> { - if matches!(expr.kind, hir::ExprKind::Lit(_)) { - return Some(LiteralIntegerTy::Value(expr)); + /// If `expr` is not a literal integer like `1`, returns `None`. + fn literal_integer(expr: &hir::Expr<'_>) -> Option { + if let hir::ExprKind::Lit(ref lit) = expr.kind && let ast::LitKind::Int(n, _) = lit.node { + Some(n) } - if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind { - return Some(LiteralIntegerTy::Ref(inn)); + else { + None } - None } /// Manages when the lint should be triggered. Operations in constant environments, hard coded @@ -117,10 +102,20 @@ impl ArithmeticSideEffects { return; } let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { - match (Self::literal_integer(lhs), Self::literal_integer(rhs)) { - (None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()), - (Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true, - (None, None) | (Some(_), Some(_)) => false, + let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); + let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); + match (Self::literal_integer(actual_lhs), Self::literal_integer(actual_rhs)) { + (None, None) => false, + (None, Some(n)) | (Some(n), None) => match (&op.node, n) { + (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) + | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) + | (hir::BinOpKind::Mul, 0 | 1) => true, + _ => false, + }, + (Some(_), Some(_)) => { + matches!((lhs_ref_counter, rhs_ref_counter), (0, 0)) + }, } } else { false @@ -129,21 +124,45 @@ impl ArithmeticSideEffects { self.issue_lint(cx, expr); } } + + fn manage_unary_ops<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + expr: &hir::Expr<'tcx>, + un_expr: &hir::Expr<'tcx>, + un_op: hir::UnOp, + ) { + let hir::UnOp::Neg = un_op else { return; }; + if constant(cx, cx.typeck_results(), un_expr).is_some() { + return; + } + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + if self.is_allowed_ty(ty) { + return; + } + let actual_un_expr = peel_hir_expr_refs(un_expr).0; + if Self::literal_integer(actual_un_expr).is_some() { + return; + } + self.issue_lint(cx, expr); + } + + fn should_skip_expr(&mut self, expr: &hir::Expr<'_>) -> bool { + self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) + } } impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { - if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) { + if self.should_skip_expr(expr) { return; } match &expr.kind { - hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => { + hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => { self.manage_bin_ops(cx, expr, op, lhs, rhs); }, - hir::ExprKind::Unary(hir::UnOp::Neg, _) => { - if constant_simple(cx, cx.typeck_results(), expr).is_none() { - self.issue_lint(cx, expr); - } + hir::ExprKind::Unary(un_op, un_expr) => { + self.manage_unary_ops(cx, expr, un_expr, *un_op); }, _ => {}, } @@ -177,22 +196,3 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { } } } - -/// Tells if an expression is a integer declared by value or by reference. -/// -/// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather -/// than `hirExprKind::Addr`. -enum LiteralIntegerTy<'expr, 'tcx> { - /// For example, `&199` - Ref(&'expr hir::Expr<'tcx>), - /// For example, `1` or `i32::MAX` - Value(&'expr hir::Expr<'tcx>), -} - -impl<'expr, 'tcx> From> for &'expr hir::Expr<'tcx> { - fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self { - match from { - LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem, - } - } -} diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs index 71b31b5e4a56..d7917e86a861 100644 --- a/clippy_lints/src/operators/op_ref.rs +++ b/clippy_lints/src/operators/op_ref.rs @@ -199,7 +199,7 @@ fn in_impl<'tcx>( } } -fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { +fn are_equal(cx: &LateContext<'_>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { if_chain! { if let ty::Adt(adt_def, _) = middle_ty.kind(); if let Some(local_did) = adt_def.did().as_local(); diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 4eb42da1fed0..472f52380bbf 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -213,11 +213,14 @@ fn try_convert_match<'tcx>( cx: &LateContext<'tcx>, arms: &[Arm<'tcx>], ) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { - if arms.len() == 2 { - return if is_none_or_err_arm(cx, &arms[1]) { - Some((arms[0].pat, arms[0].body, arms[1].body)) - } else if is_none_or_err_arm(cx, &arms[0]) { - Some((arms[1].pat, arms[1].body, arms[0].body)) + if let [first_arm, second_arm] = arms + && first_arm.guard.is_none() + && second_arm.guard.is_none() + { + return if is_none_or_err_arm(cx, second_arm) { + Some((first_arm.pat, first_arm.body, second_arm.body)) + } else if is_none_or_err_arm(cx, first_arm) { + Some((second_arm.pat, second_arm.body, first_arm.body)) } else { None }; diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 6810a2431758..456ded3fc026 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// if f.is_some() { "yay" } else { "nay" } /// } /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub PARTIALEQ_TO_NONE, style, "Binary comparison to `Option::None` relies on `T: PartialEq`, which is unneeded" diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index a4d265111f9a..97b5a4ce3641 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -130,7 +130,7 @@ enum DerefPossible { Impossible, } -fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool { +fn apply_lint(cx: &LateContext<'_>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool { let maybe_mismatch = find_first_mismatch(cx, pat); if let Some((span, mutability, level)) = maybe_mismatch { span_lint_and_help( @@ -163,7 +163,7 @@ enum Level { Lower, } -fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> { +fn find_first_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> { let mut result = None; pat.walk(|p| { if result.is_some() { diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 72dda67c72b2..47b8891e1230 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -105,17 +105,17 @@ fn expr_as_ptr_offset_call<'tcx>( } // Is the type of the expression a usize? -fn is_expr_ty_usize<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { +fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize } // Is the type of the expression a raw pointer? -fn is_expr_ty_raw_ptr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { +fn is_expr_ty_raw_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { cx.typeck_results().expr_ty(expr).is_unsafe_ptr() } -fn build_suggestion<'tcx>( - cx: &LateContext<'tcx>, +fn build_suggestion( + cx: &LateContext<'_>, method: Method, receiver_expr: &Expr<'_>, cast_lhs_expr: &Expr<'_>, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index bb86fb3b7d42..5269bbd1f1ac 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -189,6 +189,7 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) || is_res_lang_ctor(cx, res, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) + && if_else.is_none() }, _ => false, } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 4cbe9597c539..8e675d34a183 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -105,8 +105,8 @@ impl EarlyLintPass for RedundantClosureCall { impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - fn count_closure_usage<'a, 'tcx>( - cx: &'a LateContext<'tcx>, + fn count_closure_usage<'tcx>( + cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>, ) -> usize { diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 26075e9f70fa..833dc4913b46 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -70,7 +70,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { } if let ItemKind::Mod { .. } = item.kind { - self.is_exported.push(cx.effective_visibilities.is_exported(item.owner_id.def_id)); + self.is_exported + .push(cx.effective_visibilities.is_exported(item.owner_id.def_id)); } } diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 76d6ad0b23e6..8e214218f23a 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -11,8 +11,6 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::disallowed_method", "clippy::disallowed_methods"), ("clippy::disallowed_type", "clippy::disallowed_types"), ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - ("clippy::for_loop_over_option", "for_loops_over_fallibles"), - ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), @@ -31,10 +29,13 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::to_string_in_display", "clippy::recursive_format_impl"), ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::drop_bounds", "drop_bounds"), + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), + ("clippy::let_underscore_drop", "let_underscore_drop"), ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), ("clippy::panic_params", "non_fmt_panics"), ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 66b79513032f..2036e85db7e8 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; declare_clippy_lint! { @@ -33,51 +34,32 @@ declare_clippy_lint! { "imports with single component path are redundant" } -declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); +#[derive(Default)] +pub struct SingleComponentPathImports { + /// Buffer found usages to emit when visiting that item so that `#[allow]` works as expected + found: NodeMap>, +} + +struct SingleUse { + name: Symbol, + span: Span, + item_id: NodeId, + can_suggest: bool, +} + +impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { if cx.sess().opts.edition < Edition::Edition2018 { return; } - check_mod(cx, &krate.items); - } -} -fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { - // keep track of imports reused with `self` keyword, - // such as `self::crypto_hash` in the example below - // ```rust,ignore - // use self::crypto_hash::{Algorithm, Hasher}; - // ``` - let mut imports_reused_with_self = Vec::new(); - - // keep track of single use statements - // such as `crypto_hash` in the example below - // ```rust,ignore - // use crypto_hash; - // ``` - let mut single_use_usages = Vec::new(); - - // keep track of macros defined in the module as we don't want it to trigger on this (#7106) - // ```rust,ignore - // macro_rules! foo { () => {} }; - // pub(crate) use foo; - // ``` - let mut macros = Vec::new(); - - for item in items { - track_uses( - cx, - item, - &mut imports_reused_with_self, - &mut single_use_usages, - &mut macros, - ); + self.check_mod(cx, &krate.items); } - for (name, span, can_suggest) in single_use_usages { - if !imports_reused_with_self.contains(&name) { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + for SingleUse { span, can_suggest, .. } in self.found.remove(&item.id).into_iter().flatten() { if can_suggest { span_lint_and_sugg( cx, @@ -102,74 +84,127 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { } } -fn track_uses( - cx: &EarlyContext<'_>, - item: &Item, - imports_reused_with_self: &mut Vec, - single_use_usages: &mut Vec<(Symbol, Span, bool)>, - macros: &mut Vec, -) { - if item.span.from_expansion() || item.vis.kind.is_pub() { - return; - } +impl SingleComponentPathImports { + fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P]) { + // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example + // below. Removing the `use crypto_hash;` would make this a compile error + // ``` + // use crypto_hash; + // + // use self::crypto_hash::{Algorithm, Hasher}; + // ``` + let mut imports_reused_with_self = Vec::new(); - match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { - check_mod(cx, items); - }, - ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { - macros.push(item.ident.name); - }, - ItemKind::Use(use_tree) => { - let segments = &use_tree.prefix.segments; - - // keep track of `use some_module;` usages - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = use_tree.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push((name, item.span, true)); - } - } - return; + // keep track of single use statements such as `crypto_hash` in the example below + // ``` + // use crypto_hash; + // ``` + let mut single_use_usages = Vec::new(); + + // keep track of macros defined in the module as we don't want it to trigger on this (#7106) + // ``` + // macro_rules! foo { () => {} }; + // pub(crate) use foo; + // ``` + let mut macros = Vec::new(); + + for item in items { + self.track_uses( + cx, + item, + &mut imports_reused_with_self, + &mut single_use_usages, + &mut macros, + ); + } + + for usage in single_use_usages { + if !imports_reused_with_self.contains(&usage.name) { + self.found.entry(usage.item_id).or_default().push(usage); } + } + } - if segments.is_empty() { - // keep track of `use {some_module, some_other_module};` usages - if let UseTreeKind::Nested(trees) = &use_tree.kind { - for tree in trees { - let segments = &tree.0.prefix.segments; - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = tree.0.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push((name, tree.0.span, false)); - } - } + fn track_uses( + &mut self, + cx: &EarlyContext<'_>, + item: &Item, + imports_reused_with_self: &mut Vec, + single_use_usages: &mut Vec, + macros: &mut Vec, + ) { + if item.span.from_expansion() || item.vis.kind.is_pub() { + return; + } + + match &item.kind { + ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + self.check_mod(cx, items); + }, + ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { + macros.push(item.ident.name); + }, + ItemKind::Use(use_tree) => { + let segments = &use_tree.prefix.segments; + + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: item.span, + item_id: item.id, + can_suggest: true, + }); } } + return; } - } else { - // keep track of `use self::some_module` usages - if segments[0].ident.name == kw::SelfLower { - // simple case such as `use self::module::SomeStruct` - if segments.len() > 1 { - imports_reused_with_self.push(segments[1].ident.name); - return; - } - // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if segments.is_empty() { + // keep track of `use {some_module, some_other_module};` usages if let UseTreeKind::Nested(trees) = &use_tree.kind { for tree in trees { let segments = &tree.0.prefix.segments; - if !segments.is_empty() { - imports_reused_with_self.push(segments[0].ident.name); + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = tree.0.kind { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: tree.0.span, + item_id: item.id, + can_suggest: false, + }); + } + } + } + } + } + } else { + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + imports_reused_with_self.push(segments[1].ident.name); + return; + } + + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + imports_reused_with_self.push(segments[0].ident.name); + } } } } } - } - }, - _ => {}, + }, + _ => {}, + } } } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 760399231513..a2109038a057 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -168,7 +168,7 @@ impl SlowVectorInit { }; } - fn emit_lint<'tcx>(cx: &LateContext<'tcx>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { + fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len"); span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { diff --git a/clippy_lints/src/suspicious_xor_used_as_pow.rs b/clippy_lints/src/suspicious_xor_used_as_pow.rs new file mode 100644 index 000000000000..301aa5798bf5 --- /dev/null +++ b/clippy_lints/src/suspicious_xor_used_as_pow.rs @@ -0,0 +1,53 @@ +use clippy_utils::{numeric_literal::NumericLiteral, source::snippet_with_context}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal. + /// ### Why is this bad? + /// It's most probably a typo and may lead to unexpected behaviours. + /// ### Example + /// ```rust + /// let x = 3_i32 ^ 4_i32; + /// ``` + /// Use instead: + /// ```rust + /// let x = 3_i32.pow(4); + /// ``` + #[clippy::version = "1.66.0"] + pub SUSPICIOUS_XOR_USED_AS_POW, + restriction, + "XOR (`^`) operator possibly used as exponentiation operator" +} +declare_lint_pass!(ConfusingXorAndPow => [SUSPICIOUS_XOR_USED_AS_POW]); + +impl LateLintPass<'_> for ConfusingXorAndPow { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if !in_external_macro(cx.sess(), expr.span) && + let ExprKind::Binary(op, left, right) = &expr.kind && + op.node == BinOpKind::BitXor && + left.span.ctxt() == right.span.ctxt() && + let ExprKind::Lit(lit_left) = &left.kind && + let ExprKind::Lit(lit_right) = &right.kind && + let snip_left = snippet_with_context(cx, lit_left.span, lit_left.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) && + let snip_right = snippet_with_context(cx, lit_right.span, lit_right.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) && + let Some(left_val) = NumericLiteral::from_lit_kind(&snip_left.0, &lit_left.node) && + let Some(right_val) = NumericLiteral::from_lit_kind(&snip_right.0, &lit_right.node) && + left_val.is_decimal() && + right_val.is_decimal() { + clippy_utils::diagnostics::span_lint_and_sugg( + cx, + SUSPICIOUS_XOR_USED_AS_POW, + expr.span, + "`^` is not the exponentiation operator", + "did you mean to write", + format!("{}.pow({})", left_val.format(), right_val.format()), + Applicability::MaybeIncorrect, + ); + } + } +} diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index f46c21e12655..c374529d1ea9 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; @@ -16,6 +16,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for manual swapping. /// + /// Note that the lint will not be emitted in const blocks, as the suggestion would not be applicable. + /// /// ### Why is this bad? /// The `std::mem::swap` function exposes the intent better /// without deinitializing or copying either variable. @@ -138,6 +140,10 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa /// Implementation of the `MANUAL_SWAP` lint. fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { + if in_constant(cx, block.hir_id) { + return; + } + for w in block.stmts.windows(3) { if_chain! { // let t = foo(); diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index 8cf3efc8dc73..63b326048a48 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{HirId, Item, ItemKind}; +use clippy_utils::has_repr_attr; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Const; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -72,7 +72,3 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_ } } } - -fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr)) -} diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 3d4bbbf648c6..34642f4b122f 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -27,32 +27,24 @@ pub(super) fn check<'tcx>( // `Repr(C)` <-> unordered type. // If the first field of the `Repr(C)` type matches then the transmute is ok - ( - ReducedTy::OrderedFields(_, Some(from_sub_ty)), - ReducedTy::UnorderedFields(to_sub_ty), - ) - | ( - ReducedTy::UnorderedFields(from_sub_ty), - ReducedTy::OrderedFields(_, Some(to_sub_ty)), - ) => { + (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::UnorderedFields(to_sub_ty)) + | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => { from_ty = from_sub_ty; to_ty = to_sub_ty; continue; - } - (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) - if reduced_tys.to_fat_ptr => - { + }, + (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; continue; - } + }, (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) if reduced_tys.from_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; continue; - } + }, // ptr <-> ptr (ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty)) @@ -62,19 +54,19 @@ pub(super) fn check<'tcx>( from_ty = from_sub_ty; to_ty = to_sub_ty; continue; - } + }, // fat ptr <-> (*size, *size) (ReducedTy::Other(_), ReducedTy::UnorderedFields(to_ty)) if reduced_tys.from_fat_ptr && is_size_pair(to_ty) => { return false; - } + }, (ReducedTy::UnorderedFields(from_ty), ReducedTy::Other(_)) if reduced_tys.to_fat_ptr && is_size_pair(from_ty) => { return false; - } + }, // fat ptr -> some struct | some struct -> fat ptr (ReducedTy::Other(_), _) if reduced_tys.from_fat_ptr => { @@ -85,14 +77,12 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty.peel_refs() { - diag.note(&format!( - "the contained type `{from_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } }, ); return true; - } + }, (_, ReducedTy::Other(_)) if reduced_tys.to_fat_ptr => { span_lint_and_then( cx, @@ -101,18 +91,14 @@ pub(super) fn check<'tcx>( &format!("transmute to `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty.peel_refs() { - diag.note(&format!( - "the contained type `{to_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } }, ); return true; - } + }, - (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) - if from_ty != to_ty => - { + (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => { let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs)) = (from_ty.kind(), to_ty.kind()) && from_def == to_def @@ -139,25 +125,19 @@ pub(super) fn check<'tcx>( )); } else { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!( - "the contained type `{from_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!( - "the contained type `{to_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } } }, ); return true; - } + }, ( ReducedTy::UnorderedFields(from_ty), - ReducedTy::Other(_) - | ReducedTy::OrderedFields(..) - | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true }, ) => { span_lint_and_then( cx, @@ -166,18 +146,14 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!( - "the contained type `{from_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } }, ); return true; - } + }, ( - ReducedTy::Other(_) - | ReducedTy::OrderedFields(..) - | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true }, ReducedTy::UnorderedFields(to_ty), ) => { span_lint_and_then( @@ -187,25 +163,19 @@ pub(super) fn check<'tcx>( &format!("transmute into `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!( - "the contained type `{to_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } }, ); return true; - } + }, ( - ReducedTy::OrderedFields(..) - | ReducedTy::Other(_) - | ReducedTy::TypeErasure { raw_ptr_only: true }, - ReducedTy::OrderedFields(..) - | ReducedTy::Other(_) - | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true }, ) | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => { break; - } + }, } } @@ -223,38 +193,42 @@ struct ReducedTys<'tcx> { } /// Remove references so long as both types are references. -fn reduce_refs<'tcx>( - cx: &LateContext<'tcx>, - mut from_ty: Ty<'tcx>, - mut to_ty: Ty<'tcx>, -) -> ReducedTys<'tcx> { +fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: Ty<'tcx>) -> ReducedTys<'tcx> { let mut from_raw_ptr = false; let mut to_raw_ptr = false; - let (from_fat_ptr, to_fat_ptr) = - loop { - break match (from_ty.kind(), to_ty.kind()) { - ( - &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })), - &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })), - ) => { - from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_)); - from_ty = from_sub_ty; - to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_)); - to_ty = to_sub_ty; - continue; - } - ( - &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), - _, - ) if !unsized_ty.is_sized(cx.tcx, cx.param_env) => (true, false), - ( - _, - &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), - ) if !unsized_ty.is_sized(cx.tcx, cx.param_env) => (false, true), - _ => (false, false), - }; + let (from_fat_ptr, to_fat_ptr) = loop { + break match (from_ty.kind(), to_ty.kind()) { + ( + &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })), + &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })), + ) => { + from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_)); + from_ty = from_sub_ty; + to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_)); + to_ty = to_sub_ty; + continue; + }, + (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _) + if !unsized_ty.is_sized(cx.tcx, cx.param_env) => + { + (true, false) + }, + (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))) + if !unsized_ty.is_sized(cx.tcx, cx.param_env) => + { + (false, true) + }, + _ => (false, false), }; - ReducedTys { from_ty, to_ty, from_raw_ptr, to_raw_ptr, from_fat_ptr, to_fat_ptr } + }; + ReducedTys { + from_ty, + to_ty, + from_raw_ptr, + to_raw_ptr, + from_fat_ptr, + to_fat_ptr, + } } enum ReducedTy<'tcx> { @@ -277,11 +251,11 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> return match *ty.kind() { ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => { ReducedTy::TypeErasure { raw_ptr_only: false } - } + }, ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { ty = sub_ty; continue; - } + }, ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure { raw_ptr_only: false }, ty::Tuple(args) => { let mut iter = args.iter(); @@ -293,7 +267,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> continue; } ReducedTy::UnorderedFields(ty) - } + }, ty::Adt(def, substs) if def.is_struct() => { let mut iter = def .non_enum_variant() @@ -312,12 +286,10 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> } else { ReducedTy::UnorderedFields(ty) } - } - ty::Adt(def, _) - if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => - { + }, + ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => { ReducedTy::TypeErasure { raw_ptr_only: false } - } + }, // TODO: Check if the conversion to or from at least one of a union's fields is valid. ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure { raw_ptr_only: false }, ty::Foreign(_) | ty::Param(_) => ReducedTy::TypeErasure { raw_ptr_only: false }, @@ -356,11 +328,7 @@ fn same_except_params<'tcx>(subs1: SubstsRef<'tcx>, subs2: SubstsRef<'tcx>) -> b for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) { match (ty1.kind(), ty2.kind()) { (ty::Param(_), _) | (_, ty::Param(_)) => (), - (ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) - if adt1 == adt2 && same_except_params(subs1, subs2) => - { - () - } + (ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) if adt1 == adt2 && same_except_params(subs1, subs2) => (), _ => return false, } } diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 641cdf5d330e..49d863ec03f1 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,9 +1,9 @@ +use rustc_hir as hir; use rustc_hir::Expr; use rustc_hir_typeck::{cast, FnCtxt, Inherited}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; -use rustc_hir as hir; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment @@ -55,9 +55,14 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx> ); if let Ok(check) = cast::CastCheck::new( - &fn_ctxt, e, from_ty, to_ty, + &fn_ctxt, + e, + from_ty, + to_ty, // We won't show any error to the user, so we don't care what the span is here. - DUMMY_SP, DUMMY_SP, hir::Constness::NotConst, + DUMMY_SP, + DUMMY_SP, + hir::Constness::NotConst, ) { let res = check.do_check(&fn_ctxt); diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 802415e163df..43665a922d44 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -39,12 +39,19 @@ fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option let id = path_def_id(cx, param)?; cx.tcx .get_diagnostic_name(id) - .filter(|&name| matches!(name, sym::HashMap | sym::Vec | sym::HashSet - | sym::VecDeque - | sym::LinkedList - | sym::BTreeMap - | sym::BTreeSet - | sym::BinaryHeap)) + .filter(|&name| { + matches!( + name, + sym::HashMap + | sym::Vec + | sym::HashSet + | sym::VecDeque + | sym::LinkedList + | sym::BTreeMap + | sym::BTreeSet + | sym::BinaryHeap + ) + }) .or_else(|| { cx.tcx .lang_items() diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index f6de87b0526c..20978e81dc58 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -379,7 +379,9 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - let is_exported = cx.effective_visibilities.is_exported(cx.tcx.hir().local_def_id(field.hir_id)); + let is_exported = cx + .effective_visibilities + .is_exported(cx.tcx.hir().local_def_id(field.hir_id)); self.check_ty( cx, diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 2b964b64a330..fae5385ffc84 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -5,16 +5,12 @@ use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; +use rustc_middle::ty::TypeVisitable; use rustc_span::symbol::sym; use super::{utils, REDUNDANT_ALLOCATION}; -pub(super) fn check( - cx: &LateContext<'_>, - hir_ty: &hir::Ty<'_>, - qpath: &QPath<'_>, - def_id: DefId, -) -> bool { +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { let mut applicability = Applicability::MaybeIncorrect; let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { "Box" @@ -34,12 +30,7 @@ pub(super) fn check( hir_ty.span, &format!("usage of `{outer_sym}<{generic_snippet}>`"), |diag| { - diag.span_suggestion( - hir_ty.span, - "try", - format!("{generic_snippet}"), - applicability, - ); + diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability); diag.note(&format!( "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap" )); @@ -61,15 +52,16 @@ pub(super) fn check( return false }; let inner_span = match qpath_generic_tys(inner_qpath).next() { - Some(ty) => { + Some(hir_ty) => { // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use // here because `mod.rs` guarantees this lint is only run on types outside of bodies and // is not run on locals. - if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx, cx.param_env) { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if ty.has_escaping_bound_vars() || !ty.is_sized(cx.tcx, cx.param_env) { return false; } - ty.span - } + hir_ty.span + }, None => return false, }; if inner_sym == outer_sym { diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 9ad2cb853d39..7a3c7cd8a99f 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -42,7 +42,7 @@ pub(super) fn check( if !ty_ty.has_escaping_bound_vars(); if ty_ty.is_sized(cx.tcx, cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); - if ty_ty_size <= box_size_threshold; + if ty_ty_size < box_size_threshold; then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index d2e675a783ea..e8f15a444735 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -68,7 +68,8 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { && !in_external_macro(cx.tcx.sess, block.span) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) && !is_unsafe_from_proc_macro(cx, block.span) - && !block_has_safety_comment(cx, block) + && !block_has_safety_comment(cx, block.span) + && !block_parents_have_safety_comment(cx, block.hir_id) { let source_map = cx.tcx.sess.source_map(); let span = if source_map.is_multiline(block.span) { @@ -126,8 +127,41 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { .map_or(true, |src| !src.starts_with("unsafe")) } +// Checks if any parent {expression, statement, block, local, const, static} +// has a safety comment +fn block_parents_have_safety_comment(cx: &LateContext<'_>, id: hir::HirId) -> bool { + if let Some(node) = get_parent_node(cx.tcx, id) { + return match node { + Node::Expr(expr) => !is_branchy(expr) && span_in_body_has_safety_comment(cx, expr.span), + Node::Stmt(hir::Stmt { + kind: + hir::StmtKind::Local(hir::Local { span, .. }) + | hir::StmtKind::Expr(hir::Expr { span, .. }) + | hir::StmtKind::Semi(hir::Expr { span, .. }), + .. + }) + | Node::Local(hir::Local { span, .. }) + | Node::Item(hir::Item { + kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + span, + .. + }) => span_in_body_has_safety_comment(cx, *span), + _ => false, + }; + } + false +} + +/// Checks if an expression is "branchy", e.g. loop, match/if/etc. +fn is_branchy(expr: &hir::Expr<'_>) -> bool { + matches!( + expr.kind, + hir::ExprKind::If(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..) + ) +} + /// Checks if the lines immediately preceding the block contain a safety comment. -fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> bool { +fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // This intentionally ignores text before the start of a function so something like: // ``` // // SAFETY: reason @@ -136,7 +170,7 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> boo // won't work. This is to avoid dealing with where such a comment should be place relative to // attributes and doc comments. - span_from_macro_expansion_has_safety_comment(cx, block.span) || span_in_body_has_safety_comment(cx, block.span) + span_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span) } /// Checks if the lines immediately preceding the item contain a safety comment. diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 32cd46812014..952586527689 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -50,7 +50,7 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { }, UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {}, UseTreeKind::Nested(ref nested_use_tree) => { - for &(ref use_tree, _) in nested_use_tree { + for (use_tree, _) in nested_use_tree { check_use_tree(use_tree, cx, span); } }, diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index b452be084094..4ee16d9a5e4a 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// // ... /// } /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub UNUSED_PEEKABLE, nursery, "creating a peekable iterator without using any of its methods" diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 5ab351bc29ca..aac6719a8dc0 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -30,16 +30,15 @@ declare_clippy_lint! { declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { - if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind - && let method_name = seg.ident.name.as_str() + if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind + && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") && let ExprKind::Lit(token_lit) = &receiver.kind && token_lit.is_semantic_float() { - let f = token_lit.symbol.as_str().parse::().unwrap(); let mut f_str = token_lit.symbol.to_string(); - match token_lit.suffix { - Some(suffix) => f_str.push_str(suffix.as_str()), - None => {} + let f = f_str.trim_end_matches('_').parse::().unwrap(); + if let Some(suffix) = token_lit.suffix { + f_str.push_str(suffix.as_str()); } if f.fract() == 0.0 { Some((method_name, f_str)) diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index cd1d90e860b9..cad8da18c2fb 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{position_before_rarrow, snippet_opt}; use if_chain::if_chain; -use rustc_ast::ast; -use rustc_ast::visit::FnKind; +use rustc_ast::{ast, visit::FnKind, ClosureBinder}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -43,6 +42,11 @@ impl EarlyLintPass for UnusedUnit { if let ast::TyKind::Tup(ref vals) = ty.kind; if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); then { + // implicit types in closure signatures are forbidden when `for<...>` is present + if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + return; + } + lint_unneeded_unit_return(cx, ty, span); } } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index c6cdf3f85fc3..e2860db71a5a 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -30,7 +30,6 @@ declare_clippy_lint! { /// /// ### Known problems /// - Unaddressed false negative in fn bodies of trait implementations - /// - False positive with associated types in traits (#4140) /// /// ### Example /// ```rust @@ -103,6 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if parameters.as_ref().map_or(true, |params| { !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) }); + if !item.span.from_expansion(); if !is_from_proc_macro(cx, item); // expensive, should be last check then { StackItem::Check { @@ -234,24 +234,13 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { then {} else { return; } } match expr.kind { - ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res { - Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => (), - Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path), - _ => span_lint(cx, path.span), - }, - // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`) + ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), ExprKind::Call(fun, _) => { if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { - if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res { - match ctor_of { - CtorOf::Variant => lint_path_to_variant(cx, path), - CtorOf::Struct => span_lint(cx, path.span), - } - } + check_path(cx, path); } }, - // unit enum variants (`Enum::A`) - ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path), + ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), _ => (), } } @@ -267,15 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind; if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id); then { - match path.res { - Res::Def(DefKind::Ctor(ctor_of, _), ..) => match ctor_of { - CtorOf::Variant => lint_path_to_variant(cx, path), - CtorOf::Struct => span_lint(cx, path.span), - }, - Res::Def(DefKind::Variant, ..) => lint_path_to_variant(cx, path), - Res::Def(DefKind::Struct, ..) => span_lint(cx, path.span), - _ => () - } + check_path(cx, path); } } } @@ -313,6 +294,16 @@ fn span_lint(cx: &LateContext<'_>, span: Span) { ); } +fn check_path(cx: &LateContext<'_>, path: &Path<'_>) { + match path.res { + Res::Def(DefKind::Ctor(CtorOf::Variant, _) | DefKind::Variant, ..) => { + lint_path_to_variant(cx, path); + }, + Res::Def(DefKind::Ctor(CtorOf::Struct, _) | DefKind::Struct, ..) => span_lint(cx, path.span), + _ => (), + } +} + fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { if let [.., self_seg, _variant] = path.segments { let span = path diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 668123e4d6e3..b37d4239477e 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -53,11 +53,11 @@ impl DisallowedPath { path } - pub fn reason(&self) -> Option<&str> { + pub fn reason(&self) -> Option { match self { Self::WithReason { reason: Some(reason), .. - } => Some(reason), + } => Some(format!("{reason} (from clippy.toml)")), _ => None, } } @@ -213,7 +213,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), @@ -335,6 +335,12 @@ define_Conf! { /// /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other (upper_case_acronyms_aggressive: bool = false), + /// Lint: MANUAL_LET_ELSE. + /// + /// Whether the matches should be considered by the lint, and whether there should + /// be filtering for common types. + (matches_for_let_else: crate::manual_let_else::MatchLintBehaviour = + crate::manual_let_else::MatchLintBehaviour::WellKnownTypes), /// Lint: _CARGO_COMMON_METADATA. /// /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. @@ -373,23 +379,36 @@ define_Conf! { (max_include_file_size: u64 = 1_000_000), /// Lint: EXPECT_USED. /// - /// Whether `expect` should be allowed in test functions + /// Whether `expect` should be allowed within `#[cfg(test)]` (allow_expect_in_tests: bool = false), /// Lint: UNWRAP_USED. /// - /// Whether `unwrap` should be allowed in test functions + /// Whether `unwrap` should be allowed in test cfg (allow_unwrap_in_tests: bool = false), /// Lint: DBG_MACRO. /// /// Whether `dbg!` should be allowed in test functions (allow_dbg_in_tests: bool = false), - /// Lint: RESULT_LARGE_ERR + /// Lint: PRINT_STDOUT, PRINT_STDERR. + /// + /// Whether print macros (ex. `println!`) should be allowed in test functions + (allow_print_in_tests: bool = false), + /// Lint: RESULT_LARGE_ERR. /// /// The maximum size of the `Err`-variant in a `Result` returned from a function (large_error_threshold: u64 = 128), + /// Lint: MUTABLE_KEY. + /// + /// A list of paths to types that should be treated like `Arc`, i.e. ignored but + /// for the generic parameters for determining interior mutability + (ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()])), } /// Search for the configuration file. +/// +/// # Errors +/// +/// Returns any unexpected filesystem error encountered when searching for the config file pub fn lookup_conf_file() -> io::Result> { /// Possible filename to search for. const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"]; diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index 096b601572b4..4b33d492a0e4 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -2,7 +2,7 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; -use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths}; +use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { + for def_id in def_path_def_ids(cx, module) { for item in cx.tcx.module_children(def_id).iter() { if_chain! { if let Res::Def(DefKind::Const, item_def_id) = item.res; diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 22a5aa5351ad..680935f2329e 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -3,7 +3,7 @@ use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::DefKind; use rustc_hir::Item; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { // This is not a complete resolver for paths. It works on all the paths currently used in the paths // module. That's all it does and all it needs to do. pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if def_path_res(cx, path, None) != Res::Err { + if !def_path_res(cx, path).is_empty() { return true; } diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 0dac64376b06..1aebb8b3104b 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -256,7 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } } -pub(super) fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { +pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Rptr( _, MutTy { diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index cfba7fa8791d..08980cb12ed6 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; +use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; @@ -11,9 +11,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; -use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty}; +use rustc_middle::ty::{self, DefIdTree, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::Symbol; use rustc_span::Span; use std::str; @@ -110,7 +110,7 @@ impl UnnecessaryDefPath { // Extract the path to the matched type if let Some(segments) = path_to_matched_type(cx, item_arg); let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = inherent_def_path_res(cx, &segments[..]); + if let Some(def_id) = def_path_def_ids(cx, &segments[..]).next(); then { // Check if the target item is a diagnostic item or LangItem. #[rustfmt::skip] @@ -209,7 +209,7 @@ impl UnnecessaryDefPath { fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { let Some(path) = path_from_array(elements) else { return }; - if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::>()) { + for def_id in def_path_def_ids(cx, &path.iter().map(AsRef::as_ref).collect::>()) { self.array_def_ids.insert((def_id, span)); } } @@ -246,7 +246,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().values().next()?; + let &alloc = alloc.provenance().ptrs().values().next()?; if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { (alloc.inner(), ty) } else { @@ -262,6 +262,7 @@ fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation { alloc .provenance() + .ptrs() .values() .map(|&alloc| { if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { @@ -293,38 +294,6 @@ fn path_from_array(exprs: &[Expr<'_>]) -> Option> { .collect() } -// def_path_res will match field names before anything else, but for this we want to match -// inherent functions first. -fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option { - def_path_res(cx, segments, None).opt_def_id().map(|def_id| { - if cx.tcx.def_kind(def_id) == DefKind::Field { - let method_name = *segments.last().unwrap(); - cx.tcx - .def_key(def_id) - .parent - .and_then(|parent_idx| { - cx.tcx - .inherent_impls(DefId { - index: parent_idx, - krate: def_id.krate, - }) - .iter() - .find_map(|impl_id| { - cx.tcx.associated_items(*impl_id).find_by_name_and_kind( - cx.tcx, - Ident::from_str(method_name), - AssocKind::Fn, - *impl_id, - ) - }) - }) - .map_or(def_id, |item| item.def_id) - } else { - def_id - } - }) -} - fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) { Some(lang_item.variant_name()) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 36574198f917..6b321765bc08 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; +use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; @@ -232,6 +233,16 @@ declare_clippy_lint! { #[derive(Default)] pub struct Write { in_debug_impl: bool, + allow_print_in_tests: bool, +} + +impl Write { + pub fn new(allow_print_in_tests: bool) -> Self { + Self { + allow_print_in_tests, + ..Default::default() + } + } } impl_lint_pass!(Write => [ @@ -271,13 +282,15 @@ impl<'tcx> LateLintPass<'tcx> for Write { .as_ref() .map_or(false, |crate_name| crate_name == "build_script_build"); + let allowed_in_tests = self.allow_print_in_tests + && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)); match diag_name { - sym::print_macro | sym::println_macro => { + sym::print_macro | sym::println_macro if !allowed_in_tests => { if !is_build_script { span_lint(cx, PRINT_STDOUT, macro_call.span, &format!("use of `{name}!`")); } }, - sym::eprint_macro | sym::eprintln_macro => { + sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => { span_lint(cx, PRINT_STDERR, macro_call.span, &format!("use of `{name}!`")); }, sym::write_macro | sym::writeln_macro => {}, diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 83fee7bb39c2..fb9f4740ecc5 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.66" +version = "0.1.67" edition = "2021" publish = false diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 23aed4b5ba2f..939c61189ec8 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -148,11 +148,19 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), ( - MethodCall(box ast::MethodCall { seg: ls, receiver: lr, args: la, .. }), - MethodCall(box ast::MethodCall { seg: rs, receiver: rr, args: ra, .. }) - ) => { - eq_path_seg(ls, rs) && eq_expr(lr, rr) && over(la, ra, |l, r| eq_expr(l, r)) - }, + MethodCall(box ast::MethodCall { + seg: ls, + receiver: lr, + args: la, + .. + }), + MethodCall(box ast::MethodCall { + seg: rs, + receiver: rr, + args: ra, + .. + }), + ) => eq_path_seg(ls, rs) && eq_expr(lr, rr) && over(la, ra, |l, r| eq_expr(l, r)), (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), (Lit(l), Lit(r)) => l == r, @@ -191,7 +199,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { fn_decl: rf, body: re, .. - }) + }), ) => { eq_closure_binder(lb, rb) && lc == rc diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 07e4ef6a2fef..315aea9aa091 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -51,8 +51,8 @@ pub enum Constant { impl PartialEq for Constant { fn eq(&self, other: &Self) -> bool { match (self, other) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs, - (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r, + (Self::Str(ls), Self::Str(rs)) => ls == rs, + (Self::Binary(l), Self::Binary(r)) => l == r, (&Self::Char(l), &Self::Char(r)) => l == r, (&Self::Int(l), &Self::Int(r)) => l == r, (&Self::F64(l), &Self::F64(r)) => { @@ -69,8 +69,8 @@ impl PartialEq for Constant { }, (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, - (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb, + (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => ls == rs && lv == rv, + (Self::Ref(lb), Self::Ref(rb)) => *lb == *rb, // TODO: are there inter-type equalities? _ => false, } @@ -126,8 +126,8 @@ impl Hash for Constant { impl Constant { pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { match (left, right) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), - (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), + (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)), + (Self::Char(l), Self::Char(r)) => Some(l.cmp(r)), (&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() { ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))), ty::Uint(_) => Some(l.cmp(&r)), @@ -135,8 +135,8 @@ impl Constant { }, (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), - (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), - (&Self::Tuple(ref l), &Self::Tuple(ref r)) if l.len() == r.len() => match *cmp_type.kind() { + (Self::Bool(l), Self::Bool(r)) => Some(l.cmp(r)), + (Self::Tuple(l), Self::Tuple(r)) if l.len() == r.len() => match *cmp_type.kind() { ty::Tuple(tys) if tys.len() == l.len() => l .iter() .zip(r) @@ -146,17 +146,16 @@ impl Constant { .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), _ => None, }, - (&Self::Vec(ref l), &Self::Vec(ref r)) => { - let cmp_type = match *cmp_type.kind() { - ty::Array(ty, _) | ty::Slice(ty) => ty, - _ => return None, + (Self::Vec(l), Self::Vec(r)) => { + let (ty::Array(cmp_type, _) | ty::Slice(cmp_type)) = *cmp_type.kind() else { + return None }; iter::zip(l, r) .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) .find(|r| r.map_or(true, |o| o != Ordering::Equal)) .unwrap_or_else(|| Some(l.len().cmp(&r.len()))) }, - (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { + (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => { match Self::partial_cmp( tcx, match *cmp_type.kind() { @@ -170,7 +169,7 @@ impl Constant { x => x, } }, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp( + (Self::Ref(lb), Self::Ref(rb)) => Self::partial_cmp( tcx, match *cmp_type.kind() { ty::Ref(_, ty, _) => ty, @@ -401,10 +400,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { use self::Constant::{Int, F32, F64}; match *o { Int(value) => { - let ity = match *ty.kind() { - ty::Int(ity) => ity, - _ => return None, - }; + let ty::Int(ity) = *ty.kind() else { return None }; // sign extend let value = sext(self.lcx.tcx, value, ity); let value = value.checked_neg()?; diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 78f93755b72d..16b160b6fd27 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -72,8 +72,8 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( - cx: &'a T, +pub fn span_lint_and_help( + cx: &T, lint: &'static Lint, span: impl Into, msg: &str, @@ -114,8 +114,8 @@ pub fn span_lint_and_help<'a, T: LintContext>( /// 10 | forget(&SomeStruct); /// | ^^^^^^^^^^^ /// ``` -pub fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, +pub fn span_lint_and_note( + cx: &T, lint: &'static Lint, span: impl Into, msg: &str, @@ -192,8 +192,8 @@ pub fn span_lint_hir_and_then( /// = note: `-D fold-any` implied by `-D warnings` /// ``` #[cfg_attr(feature = "internal", allow(clippy::collapsible_span_lint_calls))] -pub fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, +pub fn span_lint_and_sugg( + cx: &T, lint: &'static Lint, sp: Span, msg: &str, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index cf24ec8b67b9..0231a51adf48 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -8,7 +8,7 @@ use rustc_hir::HirIdMap; use rustc_hir::{ ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, - PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, + PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; @@ -131,13 +131,10 @@ impl HirEqInterExpr<'_, '_, '_> { ([], None, [], None) => { // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro // expanded to nothing, or the cfg attribute was used. - let (left, right) = match ( + let (Some(left), Some(right)) = ( snippet_opt(self.inner.cx, left.span), snippet_opt(self.inner.cx, right.span), - ) { - (Some(left), Some(right)) => (left, right), - _ => return true, - }; + ) else { return true }; let mut left_pos = 0; let left = tokenize(&left) .map(|t| { @@ -269,7 +266,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Let(l), &ExprKind::Let(r)) => { self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) }, - (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, + (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node, (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) }, @@ -294,8 +291,8 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => { self.eq_expr(le, re) && self.eq_array_length(ll, rl) }, - (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)), - (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r), + (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l, r, |l, r| self.eq_expr(l, r)), + (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r), (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { self.eq_qpath(l_path, r_path) && both(lo, ro, |l, r| self.eq_expr(l, r)) @@ -365,7 +362,7 @@ impl HirEqInterExpr<'_, '_, '_> { } eq }, - (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), + (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { @@ -432,13 +429,11 @@ impl HirEqInterExpr<'_, '_, '_> { match (&left.kind, &right.kind) { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), - (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { - l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty) - }, - (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => { + (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty), + (TyKind::Rptr(_, l_rmut), TyKind::Rptr(_, r_rmut)) => { l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, - (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r), + (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), (&TyKind::Infer, &TyKind::Infer) => true, _ => false, @@ -1033,6 +1028,14 @@ pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 { h.finish() } +pub fn is_bool(ty: &Ty<'_>) -> bool { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + matches!(path.res, Res::PrimTy(PrimTy::Bool)) + } else { + false + } +} + pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 { let mut h = SpanlessHash::new(cx); h.hash_expr(e); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3f93b9b491d4..9e2682925a22 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -66,7 +66,7 @@ pub mod visitors; pub use self::attrs::*; pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match}; pub use self::hir_utils::{ - both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash, + both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash, }; use core::ops::ControlFlow; @@ -80,17 +80,16 @@ use rustc_ast::ast::{self, LitKind}; use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, - Destination, Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, - LangItem, Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, - QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, + self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, + Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local, + MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, + TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -109,11 +108,10 @@ use rustc_middle::ty::{FloatTy, IntTy, UintTy}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::source_map::original_sp; use rustc_span::source_map::SourceMap; use rustc_span::sym; -use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::Span; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; @@ -436,11 +434,7 @@ pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[ /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given lang item. -pub fn is_path_lang_item<'tcx>( - cx: &LateContext<'_>, - maybe_path: &impl MaybePath<'tcx>, - lang_item: LangItem, -) -> bool { +pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id)) } @@ -535,125 +529,177 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { - let single = |ty| tcx.incoherent_impls(ty).iter().copied(); - let empty = || [].iter().copied(); - match name { - "bool" => single(BoolSimplifiedType), - "char" => single(CharSimplifiedType), - "str" => single(StrSimplifiedType), - "array" => single(ArraySimplifiedType), - "slice" => single(SliceSimplifiedType), +fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { + let ty = match name { + "bool" => BoolSimplifiedType, + "char" => CharSimplifiedType, + "str" => StrSimplifiedType, + "array" => ArraySimplifiedType, + "slice" => SliceSimplifiedType, // FIXME: rustdoc documents these two using just `pointer`. // // Maybe this is something we should do here too. - "const_ptr" => single(PtrSimplifiedType(Mutability::Not)), - "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)), - "isize" => single(IntSimplifiedType(IntTy::Isize)), - "i8" => single(IntSimplifiedType(IntTy::I8)), - "i16" => single(IntSimplifiedType(IntTy::I16)), - "i32" => single(IntSimplifiedType(IntTy::I32)), - "i64" => single(IntSimplifiedType(IntTy::I64)), - "i128" => single(IntSimplifiedType(IntTy::I128)), - "usize" => single(UintSimplifiedType(UintTy::Usize)), - "u8" => single(UintSimplifiedType(UintTy::U8)), - "u16" => single(UintSimplifiedType(UintTy::U16)), - "u32" => single(UintSimplifiedType(UintTy::U32)), - "u64" => single(UintSimplifiedType(UintTy::U64)), - "u128" => single(UintSimplifiedType(UintTy::U128)), - "f32" => single(FloatSimplifiedType(FloatTy::F32)), - "f64" => single(FloatSimplifiedType(FloatTy::F64)), - _ => empty(), - } -} - -/// Resolves a def path like `std::vec::Vec`. `namespace_hint` can be supplied to disambiguate -/// between `std::vec` the module and `std::vec` the macro -/// -/// This function is expensive and should be used sparingly. -pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option) -> Res { - fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str, matches_ns: impl Fn(Res) -> bool) -> Option { - match tcx.def_kind(def_id) { - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx - .module_children(def_id) - .iter() - .find(|item| item.ident.name.as_str() == name && matches_ns(item.res.expect_non_local())) - .map(|child| child.res.expect_non_local()), - DefKind::Impl => tcx - .associated_item_def_ids(def_id) - .iter() - .copied() - .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name) - .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)), - DefKind::Struct | DefKind::Union => tcx - .adt_def(def_id) - .non_enum_variant() - .fields - .iter() - .find(|f| f.name.as_str() == name) - .map(|f| Res::Def(DefKind::Field, f.did)), - _ => None, + "const_ptr" => PtrSimplifiedType(Mutability::Not), + "mut_ptr" => PtrSimplifiedType(Mutability::Mut), + "isize" => IntSimplifiedType(IntTy::Isize), + "i8" => IntSimplifiedType(IntTy::I8), + "i16" => IntSimplifiedType(IntTy::I16), + "i32" => IntSimplifiedType(IntTy::I32), + "i64" => IntSimplifiedType(IntTy::I64), + "i128" => IntSimplifiedType(IntTy::I128), + "usize" => UintSimplifiedType(UintTy::Usize), + "u8" => UintSimplifiedType(UintTy::U8), + "u16" => UintSimplifiedType(UintTy::U16), + "u32" => UintSimplifiedType(UintTy::U32), + "u64" => UintSimplifiedType(UintTy::U64), + "u128" => UintSimplifiedType(UintTy::U128), + "f32" => FloatSimplifiedType(FloatTy::F32), + "f64" => FloatSimplifiedType(FloatTy::F64), + _ => return [].iter().copied(), + }; + + tcx.incoherent_impls(ty).iter().copied() +} + +fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { + match tcx.def_kind(def_id) { + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx + .module_children(def_id) + .iter() + .filter(|item| item.ident.name == name) + .map(|child| child.res.expect_non_local()) + .collect(), + DefKind::Impl => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name) + .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)) + .collect(), + _ => Vec::new(), + } +} + +fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec { + let hir = tcx.hir(); + + let root_mod; + let item_kind = match hir.find_by_def_id(local_id) { + Some(Node::Crate(r#mod)) => { + root_mod = ItemKind::Mod(r#mod); + &root_mod + }, + Some(Node::Item(item)) => &item.kind, + _ => return Vec::new(), + }; + + let res = |ident: Ident, owner_id: OwnerId| { + if ident.name == name { + let def_id = owner_id.to_def_id(); + Some(Res::Def(tcx.def_kind(def_id), def_id)) + } else { + None } + }; + + match item_kind { + ItemKind::Mod(r#mod) => r#mod + .item_ids + .iter() + .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.owner_id)) + .collect(), + ItemKind::Impl(r#impl) => r#impl + .items + .iter() + .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)) + .collect(), + ItemKind::Trait(.., trait_item_refs) => trait_item_refs + .iter() + .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)) + .collect(), + _ => Vec::new(), } +} + +fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { + if let Some(local_id) = def_id.as_local() { + local_item_children_by_name(tcx, local_id, name) + } else { + non_local_item_children_by_name(tcx, def_id, name) + } +} - fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option { +/// Resolves a def path like `std::vec::Vec`. +/// +/// Can return multiple resolutions when there are multiple versions of the same crate, e.g. +/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0. +/// +/// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec` +/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. +/// +/// This function is expensive and should be used sparingly. +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec { + fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator + '_ { tcx.crates(()) .iter() .copied() - .find(|&num| tcx.crate_name(num).as_str() == name) + .filter(move |&num| tcx.crate_name(num) == name) .map(CrateNum::as_def_id) } - let (base, path) = match *path { + let tcx = cx.tcx; + + let (base, mut path) = match *path { [primitive] => { - return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy); + return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; }, [base, ref path @ ..] => (base, path), - _ => return Res::Err, + _ => return Vec::new(), }; - let tcx = cx.tcx; - let starts = find_primitive(tcx, base) - .chain(find_crate(tcx, base)) + + let base_sym = Symbol::intern(base); + + let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym { + Some(LOCAL_CRATE.as_def_id()) + } else { + None + }; + + let starts = find_primitive_impls(tcx, base) + .chain(find_crates(tcx, base_sym)) + .chain(local_crate) .map(|id| Res::Def(tcx.def_kind(id), id)); - for first in starts { - let last = path - .iter() - .copied() - .enumerate() - // for each segment, find the child item - .try_fold(first, |res, (idx, segment)| { - let matches_ns = |res: Res| { - // If at the last segment in the path, respect the namespace hint - if idx == path.len() - 1 { - match namespace_hint { - Some(ns) => res.matches_ns(ns), - None => true, - } - } else { - res.matches_ns(Namespace::TypeNS) - } - }; - - let def_id = res.def_id(); - if let Some(item) = item_child_by_name(tcx, def_id, segment, matches_ns) { - Some(item) - } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { - // it is not a child item so check inherent impl items - tcx.inherent_impls(def_id) - .iter() - .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment, matches_ns)) - } else { - None - } - }); + let mut resolutions: Vec = starts.collect(); - if let Some(last) = last { - return last; - } + while let [segment, rest @ ..] = path { + path = rest; + let segment = Symbol::intern(segment); + + resolutions = resolutions + .into_iter() + .filter_map(|res| res.opt_def_id()) + .flat_map(|def_id| { + // When the current def_id is e.g. `struct S`, check the impl items in + // `impl S { ... }` + let inherent_impl_children = tcx + .inherent_impls(def_id) + .iter() + .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); + + let direct_children = item_children_by_name(tcx, def_id, segment); + + inherent_impl_children.chain(direct_children) + }) + .collect(); } - Res::Err + resolutions +} + +/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. +pub fn def_path_def_ids(cx: &LateContext<'_>, path: &[&str]) -> impl Iterator { + def_path_res(cx, path).into_iter().filter_map(|res| res.opt_def_id()) } /// Convenience function to get the `DefId` of a trait by path. @@ -661,10 +707,10 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option< /// /// This function is expensive and should be used sparingly. pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option { - match def_path_res(cx, path, Some(Namespace::TypeNS)) { + def_path_res(cx, path).into_iter().find_map(|res| match res { Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), _ => None, - } + }) } /// Gets the `hir::TraitRef` of the trait the given method is implemented for. @@ -784,9 +830,9 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< if method.ident.name == sym::new { if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { - return std_types_symbols - .iter() - .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()); + return std_types_symbols.iter().any(|&symbol| { + cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() + }); } } } @@ -963,7 +1009,7 @@ impl std::ops::BitOrAssign for CaptureKind { /// Note as this will walk up to parent expressions until the capture can be determined it should /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or /// function argument (other than a receiver). -pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind { +pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind { let mut capture = CaptureKind::Ref(Mutability::Not); pat.each_binding_or_first(&mut |_, id, span, _| match cx @@ -1260,23 +1306,6 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { .is_some() } -/// Extends the span to the beginning of the spans line, incl. whitespaces. -/// -/// ```rust -/// let x = (); -/// // ^^ -/// // will be converted to -/// let x = (); -/// // ^^^^^^^^^^^^^^ -/// ``` -fn line_span(cx: &T, span: Span) -> Span { - let span = original_sp(span, DUMMY_SP); - let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); - let line_no = source_map_and_line.line; - let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]); - span.with_lo(line_start) -} - /// Gets the parent node, if any. pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { tcx.hir().parent_iter(id).next().map(|(_, node)| node) @@ -1749,6 +1778,10 @@ pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool { attrs.iter().any(|attr| attr.has_name(symbol)) } +pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { + has_attr(cx.tcx.hir().attrs(hir_id), sym::repr) +} + pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool { let map = &tcx.hir(); let mut prev_enclosing_node = None; @@ -1821,7 +1854,7 @@ pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) } /// Checks if the given `DefId` matches the path. -pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool { +pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool { // We should probably move to Symbols in Clippy as well rather than interning every time. let path = cx.get_def_path(did); syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied()) @@ -1870,7 +1903,11 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { - matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness.is_async()) + match kind { + FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async, + FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async, + FnKind::Closure => false, + } } /// Peels away all the compiler generated code surrounding the body of an async function, diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 8b843732a236..79b19e6fb3eb 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -12,13 +12,14 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { + 1,65,0 { LET_ELSE } 1,62,0 { BOOL_THEN_SOME } 1,58,0 { FORMAT_ARGS_CAPTURE } 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, UNSIGNED_ABS } + 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS } 1,50,0 { BOOL_THEN, CLAMP } - 1,47,0 { TAU } + 1,47,0 { TAU, IS_ASCII_DIGIT_CONST } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } 1,43,0 { LOG2_10, LOG10_2 } @@ -37,4 +38,5 @@ msrv_aliases! { 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } + 1,55,0 { SEEK_REWIND } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index bc8514734304..6c09c146082a 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -115,6 +115,9 @@ pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; +pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"]; +pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"]; +pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 45b63a4aa5df..65722f142aa6 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -18,7 +18,7 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option) -> McfResult { +pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { @@ -276,9 +276,9 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B Ok(()) } -fn check_terminator<'a, 'tcx>( +fn check_terminator<'tcx>( tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, + body: &Body<'tcx>, terminator: &Terminator<'tcx>, msrv: Option, ) -> McfResult { diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index d28bd92d708b..eacfa91ba556 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -2,13 +2,12 @@ #![allow(clippy::module_name_repetitions)] -use crate::line_span; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::hygiene; -use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext}; +use rustc_span::source_map::{original_sp, SourceMap}; +use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; use std::borrow::Cow; /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. @@ -55,6 +54,23 @@ fn first_char_in_first_line(cx: &T, span: Span) -> Option(cx: &T, span: Span) -> Span { + let span = original_sp(span, DUMMY_SP); + let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); + let line_no = source_map_and_line.line; + let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]); + span.with_lo(line_start) +} + /// Returns the indentation of the line of a span /// /// ```rust,ignore diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index eefba8cd29c4..3cacdb493772 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -801,7 +801,7 @@ pub struct DerefClosure { /// Returns `None` if no such use cases have been triggered in closure body /// /// note: this only works on single line immutable closures with exactly one input parameter. -pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'_>) -> Option { +pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option { if let hir::ExprKind::Closure(&Closure { fn_decl, body, .. }) = closure.kind { let closure_body = cx.tcx.hir().body(body); // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 3a144c2bb223..897edfc5495f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,8 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, - Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, List, ParamEnv, + Predicate, PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; @@ -59,6 +60,58 @@ pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool { }) } +/// Walks into `ty` and returns `true` if any inner type is an instance of the given type, or adt +/// constructor of the same type. +/// +/// This method also recurses into opaque type predicates, so call it with `impl Trait` and `U` +/// will also return `true`. +pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => { + if inner_ty == needle { + return true; + } + + if inner_ty.ty_adt_def() == needle.ty_adt_def() { + return true; + } + + if let ty::Opaque(def_id, _) = *inner_ty.kind() { + for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { + match predicate.kind().skip_binder() { + // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through + // and check substituions to find `U`. + ty::PredicateKind::Trait(trait_predicate) => { + if trait_predicate + .trait_ref + .substs + .types() + .skip(1) // Skip the implicit `Self` generic parameter + .any(|ty| contains_ty_adt_constructor_opaque(cx, ty, needle)) + { + return true; + } + }, + // For `impl Trait`, it will register a predicate of `::Assoc = U`, + // so we check the term for `U`. + ty::PredicateKind::Projection(projection_predicate) => { + if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() { + if contains_ty_adt_constructor_opaque(cx, ty, needle) { + return true; + } + }; + }, + _ => (), + } + } + } + + false + }, + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) +} + /// Resolves `::Item` for `T` /// Do not invoke without first verifying that the type implements `Iterator` pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { @@ -701,7 +754,7 @@ impl core::ops::Add for EnumValue { } } -/// Attempts to read the given constant as though it were an an enum value. +/// Attempts to read the given constant as though it were an enum value. #[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option { if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) { @@ -782,6 +835,42 @@ pub fn for_each_top_level_late_bound_region( ty.visit_with(&mut V { index: 0, f }) } +pub struct AdtVariantInfo { + pub ind: usize, + pub size: u64, + + /// (ind, size) + pub fields_size: Vec<(usize, u64)>, +} + +impl AdtVariantInfo { + /// Returns ADT variants ordered by size + pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: &'tcx List>) -> Vec { + let mut variants_size = adt + .variants() + .iter() + .enumerate() + .map(|(i, variant)| { + let mut fields_size = variant + .fields + .iter() + .enumerate() + .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst)))) + .collect::>(); + fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size))); + + Self { + ind: i, + size: fields_size.iter().map(|(_, size)| size).sum(), + fields_size, + } + }) + .collect::>(); + variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); + variants_size + } +} + /// Gets the struct or enum variant from the given `Res` pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> { match res { @@ -876,3 +965,132 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { (Err(_), _) => 0, } } + +/// Makes the projection type for the named associated type in the given impl or trait impl. +/// +/// This function is for associated types which are "known" to exist, and as such, will only return +/// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions +/// enabled this will check that the named associated type exists, the correct number of +/// substitutions are given, and that the correct kinds of substitutions are given (lifetime, +/// constant or type). This will not check if type normalization would succeed. +pub fn make_projection<'tcx>( + tcx: TyCtxt<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: impl IntoIterator>>, +) -> Option> { + fn helper<'tcx>( + tcx: TyCtxt<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: SubstsRef<'tcx>, + ) -> Option> { + let Some(assoc_item) = tcx + .associated_items(container_id) + .find_by_name_and_kind(tcx, Ident::with_dummy_span(assoc_ty), AssocKind::Type, container_id) + else { + debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`"); + return None; + }; + #[cfg(debug_assertions)] + { + let generics = tcx.generics_of(assoc_item.def_id); + let generic_count = generics.parent_count + generics.params.len(); + let params = generics + .parent + .map_or([].as_slice(), |id| &*tcx.generics_of(id).params) + .iter() + .chain(&generics.params) + .map(|x| &x.kind); + + debug_assert!( + generic_count == substs.len(), + "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\ + note: the expected parameters are: {:#?}\n\ + the given arguments are: `{:#?}`", + assoc_item.def_id, + substs.len(), + generic_count, + params.map(GenericParamDefKind::descr).collect::>(), + substs, + ); + + if let Some((idx, (param, arg))) = params + .clone() + .zip(substs.iter().map(GenericArg::unpack)) + .enumerate() + .find(|(_, (param, arg))| { + !matches!( + (param, arg), + (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) + | (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) + | (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) + ) + }) + { + debug_assert!( + false, + "mismatched subst type at index {}: expected a {}, found `{:?}`\n\ + note: the expected parameters are {:#?}\n\ + the given arguments are {:#?}", + idx, + param.descr(), + arg, + params.map(GenericParamDefKind::descr).collect::>(), + substs, + ); + } + } + + Some(ProjectionTy { + substs, + item_def_id: assoc_item.def_id, + }) + } + helper( + tcx, + container_id, + assoc_ty, + tcx.mk_substs(substs.into_iter().map(Into::into)), + ) +} + +/// Normalizes the named associated type in the given impl or trait impl. +/// +/// This function is for associated types which are "known" to be valid with the given +/// substitutions, and as such, will only return `None` when debug assertions are disabled in order +/// to prevent ICE's. With debug assertions enabled this will check that that type normalization +/// succeeds as well as everything checked by `make_projection`. +pub fn make_normalized_projection<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: impl IntoIterator>>, +) -> Option> { + fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: ProjectionTy<'tcx>) -> Option> { + #[cfg(debug_assertions)] + if let Some((i, subst)) = ty + .substs + .iter() + .enumerate() + .find(|(_, subst)| subst.has_late_bound_regions()) + { + debug_assert!( + false, + "substs contain late-bound region at index `{i}` which can't be normalized.\n\ + use `TyCtxt::erase_late_bound_regions`\n\ + note: subst is `{subst:#?}`", + ); + return None; + } + match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.item_def_id, ty.substs)) { + Ok(ty) => Some(ty), + Err(e) => { + debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); + None + }, + } + } + helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?) +} diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 000fb51c0185..797722cfc1fc 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -73,12 +73,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { self.update(cmt); } - fn fake_read( - &mut self, - _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) {} + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } pub struct ParamBindingIdCollector { diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml new file mode 100644 index 000000000000..578109840fb7 --- /dev/null +++ b/declare_clippy_lint/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "declare_clippy_lint" +version = "0.1.67" +edition = "2021" +publish = false + +[lib] +proc-macro = true + +[dependencies] +itertools = "0.10.1" +quote = "1.0.21" +syn = "1.0.100" diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs new file mode 100644 index 000000000000..962766916dd1 --- /dev/null +++ b/declare_clippy_lint/src/lib.rs @@ -0,0 +1,173 @@ +#![feature(let_chains)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] + +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::parse::{Parse, ParseStream}; +use syn::{parse_macro_input, Attribute, Error, Ident, Lit, LitStr, Meta, Result, Token}; + +fn parse_attr(path: [&'static str; LEN], attr: &Attribute) -> Option { + if let Meta::NameValue(name_value) = attr.parse_meta().ok()? { + let path_idents = name_value.path.segments.iter().map(|segment| &segment.ident); + + if itertools::equal(path_idents, path) + && let Lit::Str(lit) = name_value.lit + { + return Some(lit); + } + } + + None +} + +struct ClippyLint { + attrs: Vec, + explanation: String, + name: Ident, + category: Ident, + description: LitStr, +} + +impl Parse for ClippyLint { + fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + + let mut in_code = false; + let mut explanation = String::new(); + let mut version = None; + for attr in &attrs { + if let Some(lit) = parse_attr(["doc"], attr) { + let value = lit.value(); + let line = value.strip_prefix(' ').unwrap_or(&value); + + if line.starts_with("```") { + explanation += "```\n"; + in_code = !in_code; + } else if !(in_code && line.starts_with("# ")) { + explanation += line; + explanation.push('\n'); + } + } else if let Some(lit) = parse_attr(["clippy", "version"], attr) { + if let Some(duplicate) = version.replace(lit) { + return Err(Error::new_spanned(duplicate, "duplicate clippy::version")); + } + } else { + return Err(Error::new_spanned(attr, "unexpected attribute")); + } + } + + input.parse::()?; + let name = input.parse()?; + input.parse::()?; + + let category = input.parse()?; + input.parse::()?; + + let description = input.parse()?; + + Ok(Self { + attrs, + explanation, + name, + category, + description, + }) + } +} + +/// Macro used to declare a Clippy lint. +/// +/// Every lint declaration consists of 4 parts: +/// +/// 1. The documentation, which is used for the website and `cargo clippy --explain` +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or +/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. +/// 4. The `description` that contains a short explanation on what's wrong with code where the +/// lint is triggered. +/// +/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are +/// enabled by default. As said in the README.md of this repository, if the lint level mapping +/// changes, please update README.md. +/// +/// # Example +/// +/// ``` +/// use rustc_session::declare_tool_lint; +/// +/// declare_clippy_lint! { +/// /// ### What it does +/// /// Checks for ... (describe what the lint matches). +/// /// +/// /// ### Why is this bad? +/// /// Supply the reason for linting the code. +/// /// +/// /// ### Example +/// /// ```rust +/// /// Insert a short example of code that triggers the lint +/// /// ``` +/// /// +/// /// Use instead: +/// /// ```rust +/// /// Insert a short example of improved code that doesn't trigger the lint +/// /// ``` +/// #[clippy::version = "1.65.0"] +/// pub LINT_NAME, +/// pedantic, +/// "description" +/// } +/// ``` +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints +#[proc_macro] +pub fn declare_clippy_lint(input: TokenStream) -> TokenStream { + let ClippyLint { + attrs, + explanation, + name, + category, + description, + } = parse_macro_input!(input as ClippyLint); + + let mut category = category.to_string(); + + let level = format_ident!( + "{}", + match category.as_str() { + "correctness" => "Deny", + "style" | "suspicious" | "complexity" | "perf" | "internal_warn" => "Warn", + "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow", + _ => panic!("unknown category {category}"), + }, + ); + + let info = if category == "internal_warn" { + None + } else { + let info_name = format_ident!("{name}_INFO"); + + (&mut category[0..1]).make_ascii_uppercase(); + let category_variant = format_ident!("{category}"); + + Some(quote! { + pub(crate) static #info_name: &'static crate::LintInfo = &crate::LintInfo { + lint: &#name, + category: crate::LintCategory::#category_variant, + explanation: #explanation, + }; + }) + }; + + let output = quote! { + declare_tool_lint! { + #(#attrs)* + pub clippy::#name, + #level, + #description, + report_in_external_macro: true + } + + #info + }; + + TokenStream::from(output) +} diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 54c1b80c42db..ee8ab7c1d7cb 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -544,34 +544,6 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, (stats_string, counter) } -/// check if the latest modification of the logfile is older than the modification date of the -/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck -fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool { - if !lintcheck_logs_path.exists() { - return true; - } - - let clippy_modified: std::time::SystemTime = { - let [cargo, driver] = paths.map(|p| { - std::fs::metadata(p) - .expect("failed to get metadata of file") - .modified() - .expect("failed to get modification date") - }); - // the oldest modification of either of the binaries - std::cmp::max(cargo, driver) - }; - - let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path) - .expect("failed to get metadata of file") - .modified() - .expect("failed to get modification date"); - - // time is represented in seconds since X - // logs_modified 2 and clippy_modified 5 means clippy binary is older and we need to recheck - logs_modified < clippy_modified -} - #[allow(clippy::too_many_lines)] fn main() { // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` @@ -594,23 +566,6 @@ fn main() { let cargo_clippy_path = fs::canonicalize(format!("target/debug/cargo-clippy{EXE_SUFFIX}")).unwrap(); let clippy_driver_path = fs::canonicalize(format!("target/debug/clippy-driver{EXE_SUFFIX}")).unwrap(); - // if the clippy bin is newer than our logs, throw away target dirs to force clippy to - // refresh the logs - if lintcheck_needs_rerun( - &config.lintcheck_results_path, - [&cargo_clippy_path, &clippy_driver_path], - ) { - let shared_target_dir = "target/lintcheck/shared_target_dir"; - // if we get an Err here, the shared target dir probably does simply not exist - if let Ok(metadata) = std::fs::metadata(shared_target_dir) { - if metadata.is_dir() { - println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir..."); - std::fs::remove_dir_all(shared_target_dir) - .expect("failed to remove target/lintcheck/shared_target_dir"); - } - } - } - // assert that clippy is found assert!( cargo_clippy_path.is_file(), @@ -678,7 +633,7 @@ fn main() { .unwrap(); let server = config.recursive.then(|| { - fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive").unwrap_or_default(); + let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); LintcheckServer::spawn(recursive_options) }); diff --git a/rust-toolchain b/rust-toolchain index 748d8a317160..a806c1564796 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-10-20" -components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] +channel = "nightly-2022-11-21" +components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/docs.rs b/src/docs.rs deleted file mode 100644 index c033ad294a38..000000000000 --- a/src/docs.rs +++ /dev/null @@ -1,606 +0,0 @@ -// autogenerated. Please look at /clippy_dev/src/update_lints.rs - -macro_rules! include_lint { - ($file_name: expr) => { - include_str!($file_name) - }; -} - -macro_rules! docs { - ($($lint_name: expr,)*) => { - pub fn explain(lint: &str) { - println!("{}", match lint { - $( - $lint_name => include_lint!(concat!("docs/", concat!($lint_name, ".txt"))), - )* - _ => "unknown lint", - }) - } - } -} - -docs! { - "absurd_extreme_comparisons", - "alloc_instead_of_core", - "allow_attributes_without_reason", - "almost_complete_letter_range", - "almost_swapped", - "approx_constant", - "arithmetic_side_effects", - "as_conversions", - "as_ptr_cast_mut", - "as_underscore", - "assertions_on_constants", - "assertions_on_result_states", - "assign_op_pattern", - "async_yields_async", - "await_holding_invalid_type", - "await_holding_lock", - "await_holding_refcell_ref", - "bad_bit_mask", - "bind_instead_of_map", - "blanket_clippy_restriction_lints", - "blocks_in_if_conditions", - "bool_assert_comparison", - "bool_comparison", - "bool_to_int_with_if", - "borrow_as_ptr", - "borrow_deref_ref", - "borrow_interior_mutable_const", - "borrowed_box", - "box_collection", - "box_default", - "boxed_local", - "branches_sharing_code", - "builtin_type_shadow", - "bytes_count_to_len", - "bytes_nth", - "cargo_common_metadata", - "case_sensitive_file_extension_comparisons", - "cast_abs_to_unsigned", - "cast_enum_constructor", - "cast_enum_truncation", - "cast_lossless", - "cast_nan_to_int", - "cast_possible_truncation", - "cast_possible_wrap", - "cast_precision_loss", - "cast_ptr_alignment", - "cast_ref_to_mut", - "cast_sign_loss", - "cast_slice_different_sizes", - "cast_slice_from_raw_parts", - "char_lit_as_u8", - "chars_last_cmp", - "chars_next_cmp", - "checked_conversions", - "clone_double_ref", - "clone_on_copy", - "clone_on_ref_ptr", - "cloned_instead_of_copied", - "cmp_nan", - "cmp_null", - "cmp_owned", - "cognitive_complexity", - "collapsible_else_if", - "collapsible_if", - "collapsible_match", - "collapsible_str_replace", - "comparison_chain", - "comparison_to_empty", - "copy_iterator", - "crate_in_macro_def", - "create_dir", - "crosspointer_transmute", - "dbg_macro", - "debug_assert_with_mut_call", - "decimal_literal_representation", - "declare_interior_mutable_const", - "default_instead_of_iter_empty", - "default_numeric_fallback", - "default_trait_access", - "default_union_representation", - "deprecated_cfg_attr", - "deprecated_semver", - "deref_addrof", - "deref_by_slicing", - "derivable_impls", - "derive_hash_xor_eq", - "derive_ord_xor_partial_ord", - "derive_partial_eq_without_eq", - "disallowed_macros", - "disallowed_methods", - "disallowed_names", - "disallowed_script_idents", - "disallowed_types", - "diverging_sub_expression", - "doc_link_with_quotes", - "doc_markdown", - "double_comparisons", - "double_must_use", - "double_neg", - "double_parens", - "drop_copy", - "drop_non_drop", - "drop_ref", - "duplicate_mod", - "duplicate_underscore_argument", - "duration_subsec", - "else_if_without_else", - "empty_drop", - "empty_enum", - "empty_line_after_outer_attr", - "empty_loop", - "empty_structs_with_brackets", - "enum_clike_unportable_variant", - "enum_glob_use", - "enum_variant_names", - "eq_op", - "equatable_if_let", - "erasing_op", - "err_expect", - "excessive_precision", - "exhaustive_enums", - "exhaustive_structs", - "exit", - "expect_fun_call", - "expect_used", - "expl_impl_clone_on_copy", - "explicit_auto_deref", - "explicit_counter_loop", - "explicit_deref_methods", - "explicit_into_iter_loop", - "explicit_iter_loop", - "explicit_write", - "extend_with_drain", - "extra_unused_lifetimes", - "fallible_impl_from", - "field_reassign_with_default", - "filetype_is_file", - "filter_map_identity", - "filter_map_next", - "filter_next", - "flat_map_identity", - "flat_map_option", - "float_arithmetic", - "float_cmp", - "float_cmp_const", - "float_equality_without_abs", - "fn_address_comparisons", - "fn_params_excessive_bools", - "fn_to_numeric_cast", - "fn_to_numeric_cast_any", - "fn_to_numeric_cast_with_truncation", - "for_kv_map", - "forget_copy", - "forget_non_drop", - "forget_ref", - "format_in_format_args", - "format_push_string", - "from_iter_instead_of_collect", - "from_over_into", - "from_str_radix_10", - "future_not_send", - "get_first", - "get_last_with_len", - "get_unwrap", - "identity_op", - "if_let_mutex", - "if_not_else", - "if_same_then_else", - "if_then_some_else_none", - "ifs_same_cond", - "implicit_clone", - "implicit_hasher", - "implicit_return", - "implicit_saturating_add", - "implicit_saturating_sub", - "imprecise_flops", - "inconsistent_digit_grouping", - "inconsistent_struct_constructor", - "index_refutable_slice", - "indexing_slicing", - "ineffective_bit_mask", - "inefficient_to_string", - "infallible_destructuring_match", - "infinite_iter", - "inherent_to_string", - "inherent_to_string_shadow_display", - "init_numbered_fields", - "inline_always", - "inline_asm_x86_att_syntax", - "inline_asm_x86_intel_syntax", - "inline_fn_without_body", - "inspect_for_each", - "int_plus_one", - "integer_arithmetic", - "integer_division", - "into_iter_on_ref", - "invalid_null_ptr_usage", - "invalid_regex", - "invalid_upcast_comparisons", - "invalid_utf8_in_unchecked", - "invisible_characters", - "is_digit_ascii_radix", - "items_after_statements", - "iter_cloned_collect", - "iter_count", - "iter_kv_map", - "iter_next_loop", - "iter_next_slice", - "iter_not_returning_iterator", - "iter_nth", - "iter_nth_zero", - "iter_on_empty_collections", - "iter_on_single_items", - "iter_overeager_cloned", - "iter_skip_next", - "iter_with_drain", - "iterator_step_by_zero", - "just_underscores_and_digits", - "large_const_arrays", - "large_digit_groups", - "large_enum_variant", - "large_include_file", - "large_stack_arrays", - "large_types_passed_by_value", - "len_without_is_empty", - "len_zero", - "let_and_return", - "let_underscore_drop", - "let_underscore_lock", - "let_underscore_must_use", - "let_unit_value", - "linkedlist", - "lossy_float_literal", - "macro_use_imports", - "main_recursion", - "manual_assert", - "manual_async_fn", - "manual_bits", - "manual_clamp", - "manual_filter", - "manual_filter_map", - "manual_find", - "manual_find_map", - "manual_flatten", - "manual_instant_elapsed", - "manual_map", - "manual_memcpy", - "manual_non_exhaustive", - "manual_ok_or", - "manual_range_contains", - "manual_rem_euclid", - "manual_retain", - "manual_saturating_arithmetic", - "manual_split_once", - "manual_str_repeat", - "manual_string_new", - "manual_strip", - "manual_swap", - "manual_unwrap_or", - "many_single_char_names", - "map_clone", - "map_collect_result_unit", - "map_entry", - "map_err_ignore", - "map_flatten", - "map_identity", - "map_unwrap_or", - "match_as_ref", - "match_bool", - "match_like_matches_macro", - "match_on_vec_items", - "match_overlapping_arm", - "match_ref_pats", - "match_result_ok", - "match_same_arms", - "match_single_binding", - "match_str_case_mismatch", - "match_wild_err_arm", - "match_wildcard_for_single_variants", - "maybe_infinite_iter", - "mem_forget", - "mem_replace_option_with_none", - "mem_replace_with_default", - "mem_replace_with_uninit", - "min_max", - "mismatched_target_os", - "mismatching_type_param_order", - "misrefactored_assign_op", - "missing_const_for_fn", - "missing_docs_in_private_items", - "missing_enforced_import_renames", - "missing_errors_doc", - "missing_inline_in_public_items", - "missing_panics_doc", - "missing_safety_doc", - "missing_spin_loop", - "missing_trait_methods", - "mistyped_literal_suffixes", - "mixed_case_hex_literals", - "mixed_read_write_in_expression", - "mod_module_files", - "module_inception", - "module_name_repetitions", - "modulo_arithmetic", - "modulo_one", - "multi_assignments", - "multiple_crate_versions", - "multiple_inherent_impl", - "must_use_candidate", - "must_use_unit", - "mut_from_ref", - "mut_mut", - "mut_mutex_lock", - "mut_range_bound", - "mutable_key_type", - "mutex_atomic", - "mutex_integer", - "naive_bytecount", - "needless_arbitrary_self_type", - "needless_bitwise_bool", - "needless_bool", - "needless_borrow", - "needless_borrowed_reference", - "needless_collect", - "needless_continue", - "needless_doctest_main", - "needless_for_each", - "needless_late_init", - "needless_lifetimes", - "needless_match", - "needless_option_as_deref", - "needless_option_take", - "needless_parens_on_range_literals", - "needless_pass_by_value", - "needless_question_mark", - "needless_range_loop", - "needless_return", - "needless_splitn", - "needless_update", - "neg_cmp_op_on_partial_ord", - "neg_multiply", - "negative_feature_names", - "never_loop", - "new_ret_no_self", - "new_without_default", - "no_effect", - "no_effect_replace", - "no_effect_underscore_binding", - "non_ascii_literal", - "non_octal_unix_permissions", - "non_send_fields_in_send_ty", - "nonminimal_bool", - "nonsensical_open_options", - "nonstandard_macro_braces", - "not_unsafe_ptr_arg_deref", - "obfuscated_if_else", - "octal_escapes", - "ok_expect", - "only_used_in_recursion", - "op_ref", - "option_as_ref_deref", - "option_env_unwrap", - "option_filter_map", - "option_if_let_else", - "option_map_or_none", - "option_map_unit_fn", - "option_option", - "or_fun_call", - "or_then_unwrap", - "out_of_bounds_indexing", - "overflow_check_conditional", - "overly_complex_bool_expr", - "panic", - "panic_in_result_fn", - "panicking_unwrap", - "partial_pub_fields", - "partialeq_ne_impl", - "partialeq_to_none", - "path_buf_push_overwrite", - "pattern_type_mismatch", - "possible_missing_comma", - "precedence", - "print_in_format_impl", - "print_literal", - "print_stderr", - "print_stdout", - "print_with_newline", - "println_empty_string", - "ptr_arg", - "ptr_as_ptr", - "ptr_eq", - "ptr_offset_with_cast", - "pub_use", - "question_mark", - "range_minus_one", - "range_plus_one", - "range_zip_with_len", - "rc_buffer", - "rc_clone_in_vec_init", - "rc_mutex", - "read_zero_byte_vec", - "recursive_format_impl", - "redundant_allocation", - "redundant_clone", - "redundant_closure", - "redundant_closure_call", - "redundant_closure_for_method_calls", - "redundant_else", - "redundant_feature_names", - "redundant_field_names", - "redundant_pattern", - "redundant_pattern_matching", - "redundant_pub_crate", - "redundant_slicing", - "redundant_static_lifetimes", - "ref_binding_to_reference", - "ref_option_ref", - "repeat_once", - "rest_pat_in_fully_bound_structs", - "result_large_err", - "result_map_or_into_option", - "result_map_unit_fn", - "result_unit_err", - "return_self_not_must_use", - "reversed_empty_ranges", - "same_functions_in_if_condition", - "same_item_push", - "same_name_method", - "search_is_some", - "self_assignment", - "self_named_constructors", - "self_named_module_files", - "semicolon_if_nothing_returned", - "separated_literal_suffix", - "serde_api_misuse", - "shadow_reuse", - "shadow_same", - "shadow_unrelated", - "short_circuit_statement", - "should_implement_trait", - "significant_drop_in_scrutinee", - "similar_names", - "single_char_add_str", - "single_char_lifetime_names", - "single_char_pattern", - "single_component_path_imports", - "single_element_loop", - "single_match", - "single_match_else", - "size_of_in_element_count", - "skip_while_next", - "slow_vector_initialization", - "stable_sort_primitive", - "std_instead_of_alloc", - "std_instead_of_core", - "str_to_string", - "string_add", - "string_add_assign", - "string_extend_chars", - "string_from_utf8_as_bytes", - "string_lit_as_bytes", - "string_slice", - "string_to_string", - "strlen_on_c_strings", - "struct_excessive_bools", - "suboptimal_flops", - "suspicious_arithmetic_impl", - "suspicious_assignment_formatting", - "suspicious_else_formatting", - "suspicious_map", - "suspicious_op_assign_impl", - "suspicious_operation_groupings", - "suspicious_splitn", - "suspicious_to_owned", - "suspicious_unary_op_formatting", - "swap_ptr_to_ref", - "tabs_in_doc_comments", - "temporary_assignment", - "to_digit_is_some", - "to_string_in_format_args", - "todo", - "too_many_arguments", - "too_many_lines", - "toplevel_ref_arg", - "trailing_empty_array", - "trait_duplication_in_bounds", - "transmute_bytes_to_str", - "transmute_float_to_int", - "transmute_int_to_bool", - "transmute_int_to_char", - "transmute_int_to_float", - "transmute_num_to_bytes", - "transmute_ptr_to_ptr", - "transmute_ptr_to_ref", - "transmute_undefined_repr", - "transmutes_expressible_as_ptr_casts", - "transmuting_null", - "trim_split_whitespace", - "trivial_regex", - "trivially_copy_pass_by_ref", - "try_err", - "type_complexity", - "type_repetition_in_bounds", - "undocumented_unsafe_blocks", - "undropped_manually_drops", - "unicode_not_nfc", - "unimplemented", - "uninit_assumed_init", - "uninit_vec", - "uninlined_format_args", - "unit_arg", - "unit_cmp", - "unit_hash", - "unit_return_expecting_ord", - "unnecessary_cast", - "unnecessary_filter_map", - "unnecessary_find_map", - "unnecessary_fold", - "unnecessary_join", - "unnecessary_lazy_evaluations", - "unnecessary_mut_passed", - "unnecessary_operation", - "unnecessary_owned_empty_strings", - "unnecessary_self_imports", - "unnecessary_sort_by", - "unnecessary_to_owned", - "unnecessary_unwrap", - "unnecessary_wraps", - "unneeded_field_pattern", - "unneeded_wildcard_pattern", - "unnested_or_patterns", - "unreachable", - "unreadable_literal", - "unsafe_derive_deserialize", - "unsafe_removed_from_name", - "unseparated_literal_suffix", - "unsound_collection_transmute", - "unused_async", - "unused_format_specs", - "unused_io_amount", - "unused_peekable", - "unused_rounding", - "unused_self", - "unused_unit", - "unusual_byte_groupings", - "unwrap_in_result", - "unwrap_or_else_default", - "unwrap_used", - "upper_case_acronyms", - "use_debug", - "use_self", - "used_underscore_binding", - "useless_asref", - "useless_attribute", - "useless_conversion", - "useless_format", - "useless_let_if_seq", - "useless_transmute", - "useless_vec", - "vec_box", - "vec_init_then_push", - "vec_resize_to_zero", - "verbose_bit_mask", - "verbose_file_reads", - "vtable_address_comparisons", - "while_immutable_condition", - "while_let_loop", - "while_let_on_iterator", - "wildcard_dependencies", - "wildcard_enum_match_arm", - "wildcard_imports", - "wildcard_in_or_patterns", - "write_literal", - "write_with_newline", - "writeln_empty_string", - "wrong_self_convention", - "wrong_transmute", - "zero_divided_by_zero", - "zero_prefixed_literal", - "zero_ptr", - "zero_sized_map_values", - "zst_offset", - -} diff --git a/src/docs/absurd_extreme_comparisons.txt b/src/docs/absurd_extreme_comparisons.txt deleted file mode 100644 index 590bee28aa23..000000000000 --- a/src/docs/absurd_extreme_comparisons.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for comparisons where one side of the relation is -either the minimum or maximum value for its type and warns if it involves a -case that is always true or always false. Only integer and boolean types are -checked. - -### Why is this bad? -An expression like `min <= x` may misleadingly imply -that it is possible for `x` to be less than the minimum. Expressions like -`max < x` are probably mistakes. - -### Known problems -For `usize` the size of the current compile target will -be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such -a comparison to detect target pointer width will trigger this lint. One can -use `mem::sizeof` and compare its value or conditional compilation -attributes -like `#[cfg(target_pointer_width = "64")] ..` instead. - -### Example -``` -let vec: Vec = Vec::new(); -if vec.len() <= 0 {} -if 100 > i32::MAX {} -``` \ No newline at end of file diff --git a/src/docs/alloc_instead_of_core.txt b/src/docs/alloc_instead_of_core.txt deleted file mode 100644 index 488a36e9276c..000000000000 --- a/src/docs/alloc_instead_of_core.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does - -Finds items imported through `alloc` when available through `core`. - -### Why is this bad? - -Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are -imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint -is also useful for crates migrating to become `no_std` compatible. - -### Example -``` -use alloc::slice::from_ref; -``` -Use instead: -``` -use core::slice::from_ref; -``` \ No newline at end of file diff --git a/src/docs/allow_attributes_without_reason.txt b/src/docs/allow_attributes_without_reason.txt deleted file mode 100644 index fcc4f49b08b3..000000000000 --- a/src/docs/allow_attributes_without_reason.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for attributes that allow lints without a reason. - -(This requires the `lint_reasons` feature) - -### Why is this bad? -Allowing a lint should always have a reason. This reason should be documented to -ensure that others understand the reasoning - -### Example -``` -#![feature(lint_reasons)] - -#![allow(clippy::some_lint)] -``` - -Use instead: -``` -#![feature(lint_reasons)] - -#![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")] -``` \ No newline at end of file diff --git a/src/docs/almost_complete_letter_range.txt b/src/docs/almost_complete_letter_range.txt deleted file mode 100644 index 01cbaf9eae25..000000000000 --- a/src/docs/almost_complete_letter_range.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for ranges which almost include the entire range of letters from 'a' to 'z', but -don't because they're a half open range. - -### Why is this bad? -This (`'a'..'z'`) is almost certainly a typo meant to include all letters. - -### Example -``` -let _ = 'a'..'z'; -``` -Use instead: -``` -let _ = 'a'..='z'; -``` \ No newline at end of file diff --git a/src/docs/almost_swapped.txt b/src/docs/almost_swapped.txt deleted file mode 100644 index cd10a8d5409b..000000000000 --- a/src/docs/almost_swapped.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for `foo = bar; bar = foo` sequences. - -### Why is this bad? -This looks like a failed attempt to swap. - -### Example -``` -a = b; -b = a; -``` -If swapping is intended, use `swap()` instead: -``` -std::mem::swap(&mut a, &mut b); -``` \ No newline at end of file diff --git a/src/docs/approx_constant.txt b/src/docs/approx_constant.txt deleted file mode 100644 index 393fa4b5ef7e..000000000000 --- a/src/docs/approx_constant.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for floating point literals that approximate -constants which are defined in -[`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants) -or -[`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants), -respectively, suggesting to use the predefined constant. - -### Why is this bad? -Usually, the definition in the standard library is more -precise than what people come up with. If you find that your definition is -actually more precise, please [file a Rust -issue](https://github.com/rust-lang/rust/issues). - -### Example -``` -let x = 3.14; -let y = 1_f64 / x; -``` -Use instead: -``` -let x = std::f32::consts::PI; -let y = std::f64::consts::FRAC_1_PI; -``` \ No newline at end of file diff --git a/src/docs/arithmetic_side_effects.txt b/src/docs/arithmetic_side_effects.txt deleted file mode 100644 index 4ae8bce88ad5..000000000000 --- a/src/docs/arithmetic_side_effects.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks any kind of arithmetic operation of any type. - -Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust -Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), -or can panic (`/`, `%`). - -Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant -environments, allowed types and non-constant operations that won't overflow are ignored. - -### Why is this bad? -For integers, overflow will trigger a panic in debug builds or wrap the result in -release mode; division by zero will cause a panic in either mode. As a result, it is -desirable to explicitly call checked, wrapping or saturating arithmetic methods. - -#### Example -``` -// `n` can be any number, including `i32::MAX`. -fn foo(n: i32) -> i32 { - n + 1 -} -``` - -Third-party types can also overflow or present unwanted side-effects. - -#### Example -``` -use rust_decimal::Decimal; -let _n = Decimal::MAX + Decimal::MAX; -``` - -### Allowed types -Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter. \ No newline at end of file diff --git a/src/docs/as_conversions.txt b/src/docs/as_conversions.txt deleted file mode 100644 index 4af479bd8111..000000000000 --- a/src/docs/as_conversions.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for usage of `as` conversions. - -Note that this lint is specialized in linting *every single* use of `as` -regardless of whether good alternatives exist or not. -If you want more precise lints for `as`, please consider using these separate lints: -`unnecessary_cast`, `cast_lossless/cast_possible_truncation/cast_possible_wrap/cast_precision_loss/cast_sign_loss`, -`fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`. -There is a good explanation the reason why this lint should work in this way and how it is useful -[in this issue](https://github.com/rust-lang/rust-clippy/issues/5122). - -### Why is this bad? -`as` conversions will perform many kinds of -conversions, including silently lossy conversions and dangerous coercions. -There are cases when it makes sense to use `as`, so the lint is -Allow by default. - -### Example -``` -let a: u32; -... -f(a as u16); -``` - -Use instead: -``` -f(a.try_into()?); - -// or - -f(a.try_into().expect("Unexpected u16 overflow in f")); -``` \ No newline at end of file diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt deleted file mode 100644 index 228dde996bb2..000000000000 --- a/src/docs/as_ptr_cast_mut.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer - -### Why is this bad? -Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior -mutability is used, making it unlikely that having it as a mutable pointer is correct. - -### Example -``` -let string = String::with_capacity(1); -let ptr = string.as_ptr() as *mut u8; -unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR -``` -Use instead: -``` -let mut string = String::with_capacity(1); -let ptr = string.as_mut_ptr(); -unsafe { ptr.write(4) }; -``` \ No newline at end of file diff --git a/src/docs/as_underscore.txt b/src/docs/as_underscore.txt deleted file mode 100644 index 2d9b0c358936..000000000000 --- a/src/docs/as_underscore.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Check for the usage of `as _` conversion using inferred type. - -### Why is this bad? -The conversion might include lossy conversion and dangerous cast that might go -undetected due to the type being inferred. - -The lint is allowed by default as using `_` is less wordy than always specifying the type. - -### Example -``` -fn foo(n: usize) {} -let n: u16 = 256; -foo(n as _); -``` -Use instead: -``` -fn foo(n: usize) {} -let n: u16 = 256; -foo(n as usize); -``` \ No newline at end of file diff --git a/src/docs/assertions_on_constants.txt b/src/docs/assertions_on_constants.txt deleted file mode 100644 index 270c1e3b639d..000000000000 --- a/src/docs/assertions_on_constants.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for `assert!(true)` and `assert!(false)` calls. - -### Why is this bad? -Will be optimized out by the compiler or should probably be replaced by a -`panic!()` or `unreachable!()` - -### Example -``` -assert!(false) -assert!(true) -const B: bool = false; -assert!(B) -``` \ No newline at end of file diff --git a/src/docs/assertions_on_result_states.txt b/src/docs/assertions_on_result_states.txt deleted file mode 100644 index 0889084fd3ad..000000000000 --- a/src/docs/assertions_on_result_states.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for `assert!(r.is_ok())` or `assert!(r.is_err())` calls. - -### Why is this bad? -An assertion failure cannot output an useful message of the error. - -### Known problems -The suggested replacement decreases the readability of code and log output. - -### Example -``` -assert!(r.is_ok()); -assert!(r.is_err()); -``` \ No newline at end of file diff --git a/src/docs/assign_op_pattern.txt b/src/docs/assign_op_pattern.txt deleted file mode 100644 index f355c0cc18d3..000000000000 --- a/src/docs/assign_op_pattern.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for `a = a op b` or `a = b commutative_op a` -patterns. - -### Why is this bad? -These can be written as the shorter `a op= b`. - -### Known problems -While forbidden by the spec, `OpAssign` traits may have -implementations that differ from the regular `Op` impl. - -### Example -``` -let mut a = 5; -let b = 0; -// ... - -a = a + b; -``` - -Use instead: -``` -let mut a = 5; -let b = 0; -// ... - -a += b; -``` \ No newline at end of file diff --git a/src/docs/async_yields_async.txt b/src/docs/async_yields_async.txt deleted file mode 100644 index a40de6d2e473..000000000000 --- a/src/docs/async_yields_async.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for async blocks that yield values of types -that can themselves be awaited. - -### Why is this bad? -An await is likely missing. - -### Example -``` -async fn foo() {} - -fn bar() { - let x = async { - foo() - }; -} -``` - -Use instead: -``` -async fn foo() {} - -fn bar() { - let x = async { - foo().await - }; -} -``` \ No newline at end of file diff --git a/src/docs/await_holding_invalid_type.txt b/src/docs/await_holding_invalid_type.txt deleted file mode 100644 index e9c768772ff6..000000000000 --- a/src/docs/await_holding_invalid_type.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Allows users to configure types which should not be held across `await` -suspension points. - -### Why is this bad? -There are some types which are perfectly "safe" to be used concurrently -from a memory access perspective but will cause bugs at runtime if they -are held in such a way. - -### Example - -``` -await-holding-invalid-types = [ - # You can specify a type name - "CustomLockType", - # You can (optionally) specify a reason - { path = "OtherCustomLockType", reason = "Relies on a thread local" } -] -``` - -``` -struct CustomLockType; -struct OtherCustomLockType; -async fn foo() { - let _x = CustomLockType; - let _y = OtherCustomLockType; - baz().await; // Lint violation -} -``` \ No newline at end of file diff --git a/src/docs/await_holding_lock.txt b/src/docs/await_holding_lock.txt deleted file mode 100644 index 0f450a11160c..000000000000 --- a/src/docs/await_holding_lock.txt +++ /dev/null @@ -1,51 +0,0 @@ -### What it does -Checks for calls to await while holding a non-async-aware MutexGuard. - -### Why is this bad? -The Mutex types found in std::sync and parking_lot -are not designed to operate in an async context across await points. - -There are two potential solutions. One is to use an async-aware Mutex -type. Many asynchronous foundation crates provide such a Mutex type. The -other solution is to ensure the mutex is unlocked before calling await, -either by introducing a scope or an explicit call to Drop::drop. - -### Known problems -Will report false positive for explicitly dropped guards -([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is -to wrap the `.lock()` call in a block instead of explicitly dropping the guard. - -### Example -``` -async fn foo(x: &Mutex) { - let mut guard = x.lock().unwrap(); - *guard += 1; - baz().await; -} - -async fn bar(x: &Mutex) { - let mut guard = x.lock().unwrap(); - *guard += 1; - drop(guard); // explicit drop - baz().await; -} -``` - -Use instead: -``` -async fn foo(x: &Mutex) { - { - let mut guard = x.lock().unwrap(); - *guard += 1; - } - baz().await; -} - -async fn bar(x: &Mutex) { - { - let mut guard = x.lock().unwrap(); - *guard += 1; - } // guard dropped here at end of scope - baz().await; -} -``` \ No newline at end of file diff --git a/src/docs/await_holding_refcell_ref.txt b/src/docs/await_holding_refcell_ref.txt deleted file mode 100644 index 226a261b9cc5..000000000000 --- a/src/docs/await_holding_refcell_ref.txt +++ /dev/null @@ -1,47 +0,0 @@ -### What it does -Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`. - -### Why is this bad? -`RefCell` refs only check for exclusive mutable access -at runtime. Holding onto a `RefCell` ref across an `await` suspension point -risks panics from a mutable ref shared while other refs are outstanding. - -### Known problems -Will report false positive for explicitly dropped refs -([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)). A workaround for this is -to wrap the `.borrow[_mut]()` call in a block instead of explicitly dropping the ref. - -### Example -``` -async fn foo(x: &RefCell) { - let mut y = x.borrow_mut(); - *y += 1; - baz().await; -} - -async fn bar(x: &RefCell) { - let mut y = x.borrow_mut(); - *y += 1; - drop(y); // explicit drop - baz().await; -} -``` - -Use instead: -``` -async fn foo(x: &RefCell) { - { - let mut y = x.borrow_mut(); - *y += 1; - } - baz().await; -} - -async fn bar(x: &RefCell) { - { - let mut y = x.borrow_mut(); - *y += 1; - } // y dropped here at end of scope - baz().await; -} -``` \ No newline at end of file diff --git a/src/docs/bad_bit_mask.txt b/src/docs/bad_bit_mask.txt deleted file mode 100644 index d40024ee5620..000000000000 --- a/src/docs/bad_bit_mask.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for incompatible bit masks in comparisons. - -The formula for detecting if an expression of the type `_ m - c` (where `` is one of {`&`, `|`} and `` is one of -{`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following -table: - -|Comparison |Bit Op|Example |is always|Formula | -|------------|------|-------------|---------|----------------------| -|`==` or `!=`| `&` |`x & 2 == 3` |`false` |`c & m != c` | -|`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` | -|`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` | -|`==` or `!=`| `\|` |`x \| 1 == 0`|`false` |`c \| m != c` | -|`<` or `>=`| `\|` |`x \| 1 < 1` |`false` |`m >= c` | -|`<=` or `>` | `\|` |`x \| 1 > 0` |`true` |`m > c` | - -### Why is this bad? -If the bits that the comparison cares about are always -set to zero or one by the bit mask, the comparison is constant `true` or -`false` (depending on mask, compared value, and operators). - -So the code is actively misleading, and the only reason someone would write -this intentionally is to win an underhanded Rust contest or create a -test-case for this lint. - -### Example -``` -if (x & 1 == 2) { } -``` \ No newline at end of file diff --git a/src/docs/bind_instead_of_map.txt b/src/docs/bind_instead_of_map.txt deleted file mode 100644 index 148575803d38..000000000000 --- a/src/docs/bind_instead_of_map.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or -`_.or_else(|x| Err(y))`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.map(|x| y)` or `_.map_err(|x| y)`. - -### Example -``` -let _ = opt().and_then(|s| Some(s.len())); -let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) }); -let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) }); -``` - -The correct use would be: - -``` -let _ = opt().map(|s| s.len()); -let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 }); -let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 }); -``` \ No newline at end of file diff --git a/src/docs/blanket_clippy_restriction_lints.txt b/src/docs/blanket_clippy_restriction_lints.txt deleted file mode 100644 index 28a4ebf7169b..000000000000 --- a/src/docs/blanket_clippy_restriction_lints.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. - -### Why is this bad? -Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust. -These lints should only be enabled on a lint-by-lint basis and with careful consideration. - -### Example -``` -#![deny(clippy::restriction)] -``` - -Use instead: -``` -#![deny(clippy::as_conversions)] -``` \ No newline at end of file diff --git a/src/docs/blocks_in_if_conditions.txt b/src/docs/blocks_in_if_conditions.txt deleted file mode 100644 index 3afa14853fd2..000000000000 --- a/src/docs/blocks_in_if_conditions.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for `if` conditions that use blocks containing an -expression, statements or conditions that use closures with blocks. - -### Why is this bad? -Style, using blocks in the condition makes it hard to read. - -### Examples -``` -if { true } { /* ... */ } - -if { let x = somefunc(); x } { /* ... */ } -``` - -Use instead: -``` -if true { /* ... */ } - -let res = { let x = somefunc(); x }; -if res { /* ... */ } -``` \ No newline at end of file diff --git a/src/docs/bool_assert_comparison.txt b/src/docs/bool_assert_comparison.txt deleted file mode 100644 index df7ca00cc2ba..000000000000 --- a/src/docs/bool_assert_comparison.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns about boolean comparisons in assert-like macros. - -### Why is this bad? -It is shorter to use the equivalent. - -### Example -``` -assert_eq!("a".is_empty(), false); -assert_ne!("a".is_empty(), true); -``` - -Use instead: -``` -assert!(!"a".is_empty()); -``` \ No newline at end of file diff --git a/src/docs/bool_comparison.txt b/src/docs/bool_comparison.txt deleted file mode 100644 index 0996f60cec44..000000000000 --- a/src/docs/bool_comparison.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for expressions of the form `x == true`, -`x != true` and order comparisons such as `x < true` (or vice versa) and -suggest using the variable directly. - -### Why is this bad? -Unnecessary code. - -### Example -``` -if x == true {} -if y == false {} -``` -use `x` directly: -``` -if x {} -if !y {} -``` \ No newline at end of file diff --git a/src/docs/bool_to_int_with_if.txt b/src/docs/bool_to_int_with_if.txt deleted file mode 100644 index 63535b454c9f..000000000000 --- a/src/docs/bool_to_int_with_if.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Instead of using an if statement to convert a bool to an int, -this lint suggests using a `from()` function or an `as` coercion. - -### Why is this bad? -Coercion or `from()` is idiomatic way to convert bool to a number. -Both methods are guaranteed to return 1 for true, and 0 for false. - -See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E - -### Example -``` -if condition { - 1_i64 -} else { - 0 -}; -``` -Use instead: -``` -i64::from(condition); -``` -or -``` -condition as i64; -``` \ No newline at end of file diff --git a/src/docs/borrow_as_ptr.txt b/src/docs/borrow_as_ptr.txt deleted file mode 100644 index 0be865abd578..000000000000 --- a/src/docs/borrow_as_ptr.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for the usage of `&expr as *const T` or -`&mut expr as *mut T`, and suggest using `ptr::addr_of` or -`ptr::addr_of_mut` instead. - -### Why is this bad? -This would improve readability and avoid creating a reference -that points to an uninitialized value or unaligned place. -Read the `ptr::addr_of` docs for more information. - -### Example -``` -let val = 1; -let p = &val as *const i32; - -let mut val_mut = 1; -let p_mut = &mut val_mut as *mut i32; -``` -Use instead: -``` -let val = 1; -let p = std::ptr::addr_of!(val); - -let mut val_mut = 1; -let p_mut = std::ptr::addr_of_mut!(val_mut); -``` \ No newline at end of file diff --git a/src/docs/borrow_deref_ref.txt b/src/docs/borrow_deref_ref.txt deleted file mode 100644 index 352480d3f26a..000000000000 --- a/src/docs/borrow_deref_ref.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for `&*(&T)`. - -### Why is this bad? -Dereferencing and then borrowing a reference value has no effect in most cases. - -### Known problems -False negative on such code: -``` -let x = &12; -let addr_x = &x as *const _ as usize; -let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggered. - // But if we fix it, assert will fail. -assert_ne!(addr_x, addr_y); -``` - -### Example -``` -let s = &String::new(); - -let a: &String = &* s; -``` - -Use instead: -``` -let a: &String = s; -``` \ No newline at end of file diff --git a/src/docs/borrow_interior_mutable_const.txt b/src/docs/borrow_interior_mutable_const.txt deleted file mode 100644 index e55b6a77e666..000000000000 --- a/src/docs/borrow_interior_mutable_const.txt +++ /dev/null @@ -1,40 +0,0 @@ -### What it does -Checks if `const` items which is interior mutable (e.g., -contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. - -### Why is this bad? -Consts are copied everywhere they are referenced, i.e., -every time you refer to the const a fresh instance of the `Cell` or `Mutex` -or `AtomicXxxx` will be created, which defeats the whole purpose of using -these types in the first place. - -The `const` value should be stored inside a `static` item. - -### Known problems -When an enum has variants with interior mutability, use of its non -interior mutable variants can generate false positives. See issue -[#3962](https://github.com/rust-lang/rust-clippy/issues/3962) - -Types that have underlying or potential interior mutability trigger the lint whether -the interior mutable field is used or not. See issues -[#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and -[#3825](https://github.com/rust-lang/rust-clippy/issues/3825) - -### Example -``` -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); - -CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged -assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct -``` - -Use instead: -``` -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); - -static STATIC_ATOM: AtomicUsize = CONST_ATOM; -STATIC_ATOM.store(9, SeqCst); -assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance -``` \ No newline at end of file diff --git a/src/docs/borrowed_box.txt b/src/docs/borrowed_box.txt deleted file mode 100644 index d7089be662a5..000000000000 --- a/src/docs/borrowed_box.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for use of `&Box` anywhere in the code. -Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. - -### Why is this bad? -A `&Box` parameter requires the function caller to box `T` first before passing it to a function. -Using `&T` defines a concrete type for the parameter and generalizes the function, this would also -auto-deref to `&T` at the function call site if passed a `&Box`. - -### Example -``` -fn foo(bar: &Box) { ... } -``` - -Better: - -``` -fn foo(bar: &T) { ... } -``` \ No newline at end of file diff --git a/src/docs/box_collection.txt b/src/docs/box_collection.txt deleted file mode 100644 index 053f24c46281..000000000000 --- a/src/docs/box_collection.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for use of `Box` where T is a collection such as Vec anywhere in the code. -Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. - -### Why is this bad? -Collections already keeps their contents in a separate area on -the heap. So if you `Box` them, you just add another level of indirection -without any benefit whatsoever. - -### Example -``` -struct X { - values: Box>, -} -``` - -Better: - -``` -struct X { - values: Vec, -} -``` \ No newline at end of file diff --git a/src/docs/box_default.txt b/src/docs/box_default.txt deleted file mode 100644 index 1c670c773337..000000000000 --- a/src/docs/box_default.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -checks for `Box::new(T::default())`, which is better written as -`Box::::default()`. - -### Why is this bad? -First, it's more complex, involving two calls instead of one. -Second, `Box::default()` can be faster -[in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). - -### Example -``` -let x: Box = Box::new(Default::default()); -``` -Use instead: -``` -let x: Box = Box::default(); -``` \ No newline at end of file diff --git a/src/docs/boxed_local.txt b/src/docs/boxed_local.txt deleted file mode 100644 index 8b1febf1455f..000000000000 --- a/src/docs/boxed_local.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for usage of `Box` where an unboxed `T` would -work fine. - -### Why is this bad? -This is an unnecessary allocation, and bad for -performance. It is only necessary to allocate if you wish to move the box -into something. - -### Example -``` -fn foo(x: Box) {} -``` - -Use instead: -``` -fn foo(x: u32) {} -``` \ No newline at end of file diff --git a/src/docs/branches_sharing_code.txt b/src/docs/branches_sharing_code.txt deleted file mode 100644 index 79be6124798a..000000000000 --- a/src/docs/branches_sharing_code.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks if the `if` and `else` block contain shared code that can be -moved out of the blocks. - -### Why is this bad? -Duplicate code is less maintainable. - -### Known problems -* The lint doesn't check if the moved expressions modify values that are being used in - the if condition. The suggestion can in that case modify the behavior of the program. - See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452) - -### Example -``` -let foo = if … { - println!("Hello World"); - 13 -} else { - println!("Hello World"); - 42 -}; -``` - -Use instead: -``` -println!("Hello World"); -let foo = if … { - 13 -} else { - 42 -}; -``` \ No newline at end of file diff --git a/src/docs/builtin_type_shadow.txt b/src/docs/builtin_type_shadow.txt deleted file mode 100644 index 15b1c9df7baa..000000000000 --- a/src/docs/builtin_type_shadow.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Warns if a generic shadows a built-in type. - -### Why is this bad? -This gives surprising type errors. - -### Example - -``` -impl Foo { - fn impl_func(&self) -> u32 { - 42 - } -} -``` \ No newline at end of file diff --git a/src/docs/bytes_count_to_len.txt b/src/docs/bytes_count_to_len.txt deleted file mode 100644 index ca7bf9a38da8..000000000000 --- a/src/docs/bytes_count_to_len.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -It checks for `str::bytes().count()` and suggests replacing it with -`str::len()`. - -### Why is this bad? -`str::bytes().count()` is longer and may not be as performant as using -`str::len()`. - -### Example -``` -"hello".bytes().count(); -String::from("hello").bytes().count(); -``` -Use instead: -``` -"hello".len(); -String::from("hello").len(); -``` \ No newline at end of file diff --git a/src/docs/bytes_nth.txt b/src/docs/bytes_nth.txt deleted file mode 100644 index 260de343353d..000000000000 --- a/src/docs/bytes_nth.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for the use of `.bytes().nth()`. - -### Why is this bad? -`.as_bytes().get()` is more efficient and more -readable. - -### Example -``` -"Hello".bytes().nth(3); -``` - -Use instead: -``` -"Hello".as_bytes().get(3); -``` \ No newline at end of file diff --git a/src/docs/cargo_common_metadata.txt b/src/docs/cargo_common_metadata.txt deleted file mode 100644 index 1998647a9274..000000000000 --- a/src/docs/cargo_common_metadata.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks to see if all common metadata is defined in -`Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata - -### Why is this bad? -It will be more difficult for users to discover the -purpose of the crate, and key information related to it. - -### Example -``` -[package] -name = "clippy" -version = "0.0.212" -repository = "https://github.com/rust-lang/rust-clippy" -readme = "README.md" -license = "MIT OR Apache-2.0" -keywords = ["clippy", "lint", "plugin"] -categories = ["development-tools", "development-tools::cargo-plugins"] -``` - -Should include a description field like: - -``` -[package] -name = "clippy" -version = "0.0.212" -description = "A bunch of helpful lints to avoid common pitfalls in Rust" -repository = "https://github.com/rust-lang/rust-clippy" -readme = "README.md" -license = "MIT OR Apache-2.0" -keywords = ["clippy", "lint", "plugin"] -categories = ["development-tools", "development-tools::cargo-plugins"] -``` \ No newline at end of file diff --git a/src/docs/case_sensitive_file_extension_comparisons.txt b/src/docs/case_sensitive_file_extension_comparisons.txt deleted file mode 100644 index 8e6e18ed4e23..000000000000 --- a/src/docs/case_sensitive_file_extension_comparisons.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for calls to `ends_with` with possible file extensions -and suggests to use a case-insensitive approach instead. - -### Why is this bad? -`ends_with` is case-sensitive and may not detect files with a valid extension. - -### Example -``` -fn is_rust_file(filename: &str) -> bool { - filename.ends_with(".rs") -} -``` -Use instead: -``` -fn is_rust_file(filename: &str) -> bool { - let filename = std::path::Path::new(filename); - filename.extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) -} -``` \ No newline at end of file diff --git a/src/docs/cast_abs_to_unsigned.txt b/src/docs/cast_abs_to_unsigned.txt deleted file mode 100644 index c5d8ee034ce5..000000000000 --- a/src/docs/cast_abs_to_unsigned.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for uses of the `abs()` method that cast the result to unsigned. - -### Why is this bad? -The `unsigned_abs()` method avoids panic when called on the MIN value. - -### Example -``` -let x: i32 = -42; -let y: u32 = x.abs() as u32; -``` -Use instead: -``` -let x: i32 = -42; -let y: u32 = x.unsigned_abs(); -``` \ No newline at end of file diff --git a/src/docs/cast_enum_constructor.txt b/src/docs/cast_enum_constructor.txt deleted file mode 100644 index 675c03a42bc2..000000000000 --- a/src/docs/cast_enum_constructor.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for casts from an enum tuple constructor to an integer. - -### Why is this bad? -The cast is easily confused with casting a c-like enum value to an integer. - -### Example -``` -enum E { X(i32) }; -let _ = E::X as usize; -``` \ No newline at end of file diff --git a/src/docs/cast_enum_truncation.txt b/src/docs/cast_enum_truncation.txt deleted file mode 100644 index abe32a8296da..000000000000 --- a/src/docs/cast_enum_truncation.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for casts from an enum type to an integral type which will definitely truncate the -value. - -### Why is this bad? -The resulting integral value will not match the value of the variant it came from. - -### Example -``` -enum E { X = 256 }; -let _ = E::X as u8; -``` \ No newline at end of file diff --git a/src/docs/cast_lossless.txt b/src/docs/cast_lossless.txt deleted file mode 100644 index c3a61dd470fc..000000000000 --- a/src/docs/cast_lossless.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for casts between numerical types that may -be replaced by safe conversion functions. - -### Why is this bad? -Rust's `as` keyword will perform many kinds of -conversions, including silently lossy conversions. Conversion functions such -as `i32::from` will only perform lossless conversions. Using the conversion -functions prevents conversions from turning into silent lossy conversions if -the types of the input expressions ever change, and make it easier for -people reading the code to know that the conversion is lossless. - -### Example -``` -fn as_u64(x: u8) -> u64 { - x as u64 -} -``` - -Using `::from` would look like this: - -``` -fn as_u64(x: u8) -> u64 { - u64::from(x) -} -``` \ No newline at end of file diff --git a/src/docs/cast_nan_to_int.txt b/src/docs/cast_nan_to_int.txt deleted file mode 100644 index 122f5da0c921..000000000000 --- a/src/docs/cast_nan_to_int.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for a known NaN float being cast to an integer - -### Why is this bad? -NaNs are cast into zero, so one could simply use this and make the -code more readable. The lint could also hint at a programmer error. - -### Example -``` -let _: (0.0_f32 / 0.0) as u64; -``` -Use instead: -``` -let _: = 0_u64; -``` \ No newline at end of file diff --git a/src/docs/cast_possible_truncation.txt b/src/docs/cast_possible_truncation.txt deleted file mode 100644 index 0b164848cc7c..000000000000 --- a/src/docs/cast_possible_truncation.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for casts between numerical types that may -truncate large values. This is expected behavior, so the cast is `Allow` by -default. - -### Why is this bad? -In some problem domains, it is good practice to avoid -truncation. This lint can be activated to help assess where additional -checks could be beneficial. - -### Example -``` -fn as_u8(x: u64) -> u8 { - x as u8 -} -``` \ No newline at end of file diff --git a/src/docs/cast_possible_wrap.txt b/src/docs/cast_possible_wrap.txt deleted file mode 100644 index f883fc9cfb99..000000000000 --- a/src/docs/cast_possible_wrap.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for casts from an unsigned type to a signed type of -the same size. Performing such a cast is a 'no-op' for the compiler, -i.e., nothing is changed at the bit level, and the binary representation of -the value is reinterpreted. This can cause wrapping if the value is too big -for the target signed type. However, the cast works as defined, so this lint -is `Allow` by default. - -### Why is this bad? -While such a cast is not bad in itself, the results can -be surprising when this is not the intended behavior, as demonstrated by the -example below. - -### Example -``` -u32::MAX as i32; // will yield a value of `-1` -``` \ No newline at end of file diff --git a/src/docs/cast_precision_loss.txt b/src/docs/cast_precision_loss.txt deleted file mode 100644 index f915d9f8a6d0..000000000000 --- a/src/docs/cast_precision_loss.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for casts from any numerical to a float type where -the receiving type cannot store all values from the original type without -rounding errors. This possible rounding is to be expected, so this lint is -`Allow` by default. - -Basically, this warns on casting any integer with 32 or more bits to `f32` -or any 64-bit integer to `f64`. - -### Why is this bad? -It's not bad at all. But in some applications it can be -helpful to know where precision loss can take place. This lint can help find -those places in the code. - -### Example -``` -let x = u64::MAX; -x as f64; -``` \ No newline at end of file diff --git a/src/docs/cast_ptr_alignment.txt b/src/docs/cast_ptr_alignment.txt deleted file mode 100644 index 6a6d4dcaa2ae..000000000000 --- a/src/docs/cast_ptr_alignment.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for casts, using `as` or `pointer::cast`, -from a less-strictly-aligned pointer to a more-strictly-aligned pointer - -### Why is this bad? -Dereferencing the resulting pointer may be undefined -behavior. - -### Known problems -Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar -on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like -u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis. - -### Example -``` -let _ = (&1u8 as *const u8) as *const u16; -let _ = (&mut 1u8 as *mut u8) as *mut u16; - -(&1u8 as *const u8).cast::(); -(&mut 1u8 as *mut u8).cast::(); -``` \ No newline at end of file diff --git a/src/docs/cast_ref_to_mut.txt b/src/docs/cast_ref_to_mut.txt deleted file mode 100644 index fb5b4dbb62d8..000000000000 --- a/src/docs/cast_ref_to_mut.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for casts of `&T` to `&mut T` anywhere in the code. - -### Why is this bad? -It’s basically guaranteed to be undefined behavior. -`UnsafeCell` is the only way to obtain aliasable data that is considered -mutable. - -### Example -``` -fn x(r: &i32) { - unsafe { - *(r as *const _ as *mut _) += 1; - } -} -``` - -Instead consider using interior mutability types. - -``` -use std::cell::UnsafeCell; - -fn x(r: &UnsafeCell) { - unsafe { - *r.get() += 1; - } -} -``` \ No newline at end of file diff --git a/src/docs/cast_sign_loss.txt b/src/docs/cast_sign_loss.txt deleted file mode 100644 index d64fe1b07f46..000000000000 --- a/src/docs/cast_sign_loss.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for casts from a signed to an unsigned numerical -type. In this case, negative values wrap around to large positive values, -which can be quite surprising in practice. However, as the cast works as -defined, this lint is `Allow` by default. - -### Why is this bad? -Possibly surprising results. You can activate this lint -as a one-time check to see where numerical wrapping can arise. - -### Example -``` -let y: i8 = -1; -y as u128; // will return 18446744073709551615 -``` \ No newline at end of file diff --git a/src/docs/cast_slice_different_sizes.txt b/src/docs/cast_slice_different_sizes.txt deleted file mode 100644 index c01ef0ba92c0..000000000000 --- a/src/docs/cast_slice_different_sizes.txt +++ /dev/null @@ -1,38 +0,0 @@ -### What it does -Checks for `as` casts between raw pointers to slices with differently sized elements. - -### Why is this bad? -The produced raw pointer to a slice does not update its length metadata. The produced -pointer will point to a different number of bytes than the original pointer because the -length metadata of a raw slice pointer is in elements rather than bytes. -Producing a slice reference from the raw pointer will either create a slice with -less data (which can be surprising) or create a slice with more data and cause Undefined Behavior. - -### Example -// Missing data -``` -let a = [1_i32, 2, 3, 4]; -let p = &a as *const [i32] as *const [u8]; -unsafe { - println!("{:?}", &*p); -} -``` -// Undefined Behavior (note: also potential alignment issues) -``` -let a = [1_u8, 2, 3, 4]; -let p = &a as *const [u8] as *const [u32]; -unsafe { - println!("{:?}", &*p); -} -``` -Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length -``` -let a = [1_i32, 2, 3, 4]; -let old_ptr = &a as *const [i32]; -// The data pointer is cast to a pointer to the target `u8` not `[u8]` -// The length comes from the known length of 4 i32s times the 4 bytes per i32 -let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16); -unsafe { - println!("{:?}", &*new_ptr); -} -``` \ No newline at end of file diff --git a/src/docs/cast_slice_from_raw_parts.txt b/src/docs/cast_slice_from_raw_parts.txt deleted file mode 100644 index b58c739766aa..000000000000 --- a/src/docs/cast_slice_from_raw_parts.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for a raw slice being cast to a slice pointer - -### Why is this bad? -This can result in multiple `&mut` references to the same location when only a pointer is -required. -`ptr::slice_from_raw_parts` is a safe alternative that doesn't require -the same [safety requirements] to be upheld. - -### Example -``` -let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _; -let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _; -``` -Use instead: -``` -let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len); -let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); -``` -[safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety \ No newline at end of file diff --git a/src/docs/char_lit_as_u8.txt b/src/docs/char_lit_as_u8.txt deleted file mode 100644 index 00d60b9a451b..000000000000 --- a/src/docs/char_lit_as_u8.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for expressions where a character literal is cast -to `u8` and suggests using a byte literal instead. - -### Why is this bad? -In general, casting values to smaller types is -error-prone and should be avoided where possible. In the particular case of -converting a character literal to u8, it is easy to avoid by just using a -byte literal instead. As an added bonus, `b'a'` is even slightly shorter -than `'a' as u8`. - -### Example -``` -'x' as u8 -``` - -A better version, using the byte literal: - -``` -b'x' -``` \ No newline at end of file diff --git a/src/docs/chars_last_cmp.txt b/src/docs/chars_last_cmp.txt deleted file mode 100644 index 4c1d8838973a..000000000000 --- a/src/docs/chars_last_cmp.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for usage of `_.chars().last()` or -`_.chars().next_back()` on a `str` to check if it ends with a given char. - -### Why is this bad? -Readability, this can be written more concisely as -`_.ends_with(_)`. - -### Example -``` -name.chars().last() == Some('_') || name.chars().next_back() == Some('-'); -``` - -Use instead: -``` -name.ends_with('_') || name.ends_with('-'); -``` \ No newline at end of file diff --git a/src/docs/chars_next_cmp.txt b/src/docs/chars_next_cmp.txt deleted file mode 100644 index 77cbce2de00f..000000000000 --- a/src/docs/chars_next_cmp.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `.chars().next()` on a `str` to check -if it starts with a given char. - -### Why is this bad? -Readability, this can be written more concisely as -`_.starts_with(_)`. - -### Example -``` -let name = "foo"; -if name.chars().next() == Some('_') {}; -``` - -Use instead: -``` -let name = "foo"; -if name.starts_with('_') {}; -``` \ No newline at end of file diff --git a/src/docs/checked_conversions.txt b/src/docs/checked_conversions.txt deleted file mode 100644 index 536b01294ee1..000000000000 --- a/src/docs/checked_conversions.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for explicit bounds checking when casting. - -### Why is this bad? -Reduces the readability of statements & is error prone. - -### Example -``` -foo <= i32::MAX as u32; -``` - -Use instead: -``` -i32::try_from(foo).is_ok(); -``` \ No newline at end of file diff --git a/src/docs/clone_double_ref.txt b/src/docs/clone_double_ref.txt deleted file mode 100644 index 2729635bd246..000000000000 --- a/src/docs/clone_double_ref.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `.clone()` on an `&&T`. - -### Why is this bad? -Cloning an `&&T` copies the inner `&T`, instead of -cloning the underlying `T`. - -### Example -``` -fn main() { - let x = vec![1]; - let y = &&x; - let z = y.clone(); - println!("{:p} {:p}", *y, z); // prints out the same pointer -} -``` \ No newline at end of file diff --git a/src/docs/clone_on_copy.txt b/src/docs/clone_on_copy.txt deleted file mode 100644 index 99a0bdb4c4ac..000000000000 --- a/src/docs/clone_on_copy.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for usage of `.clone()` on a `Copy` type. - -### Why is this bad? -The only reason `Copy` types implement `Clone` is for -generics, not for using the `clone` method on a concrete type. - -### Example -``` -42u64.clone(); -``` \ No newline at end of file diff --git a/src/docs/clone_on_ref_ptr.txt b/src/docs/clone_on_ref_ptr.txt deleted file mode 100644 index 2d83f8fefc12..000000000000 --- a/src/docs/clone_on_ref_ptr.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for usage of `.clone()` on a ref-counted pointer, -(`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified -function syntax instead (e.g., `Rc::clone(foo)`). - -### Why is this bad? -Calling '.clone()' on an Rc, Arc, or Weak -can obscure the fact that only the pointer is being cloned, not the underlying -data. - -### Example -``` -let x = Rc::new(1); - -x.clone(); -``` - -Use instead: -``` -Rc::clone(&x); -``` \ No newline at end of file diff --git a/src/docs/cloned_instead_of_copied.txt b/src/docs/cloned_instead_of_copied.txt deleted file mode 100644 index 2f2014d5fd29..000000000000 --- a/src/docs/cloned_instead_of_copied.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usages of `cloned()` on an `Iterator` or `Option` where -`copied()` could be used instead. - -### Why is this bad? -`copied()` is better because it guarantees that the type being cloned -implements `Copy`. - -### Example -``` -[1, 2, 3].iter().cloned(); -``` -Use instead: -``` -[1, 2, 3].iter().copied(); -``` \ No newline at end of file diff --git a/src/docs/cmp_nan.txt b/src/docs/cmp_nan.txt deleted file mode 100644 index e2ad04d93235..000000000000 --- a/src/docs/cmp_nan.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for comparisons to NaN. - -### Why is this bad? -NaN does not compare meaningfully to anything – not -even itself – so those comparisons are simply wrong. - -### Example -``` -if x == f32::NAN { } -``` - -Use instead: -``` -if x.is_nan() { } -``` \ No newline at end of file diff --git a/src/docs/cmp_null.txt b/src/docs/cmp_null.txt deleted file mode 100644 index 02fd15124f03..000000000000 --- a/src/docs/cmp_null.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -This lint checks for equality comparisons with `ptr::null` - -### Why is this bad? -It's easier and more readable to use the inherent -`.is_null()` -method instead - -### Example -``` -use std::ptr; - -if x == ptr::null { - // .. -} -``` - -Use instead: -``` -if x.is_null() { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/cmp_owned.txt b/src/docs/cmp_owned.txt deleted file mode 100644 index f8d4956ff1d4..000000000000 --- a/src/docs/cmp_owned.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for conversions to owned values just for the sake -of a comparison. - -### Why is this bad? -The comparison can operate on a reference, so creating -an owned value effectively throws it away directly afterwards, which is -needlessly consuming code and heap space. - -### Example -``` -if x.to_owned() == y {} -``` - -Use instead: -``` -if x == y {} -``` \ No newline at end of file diff --git a/src/docs/cognitive_complexity.txt b/src/docs/cognitive_complexity.txt deleted file mode 100644 index fdd75f6479cd..000000000000 --- a/src/docs/cognitive_complexity.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for methods with high cognitive complexity. - -### Why is this bad? -Methods of high cognitive complexity tend to be hard to -both read and maintain. Also LLVM will tend to optimize small methods better. - -### Known problems -Sometimes it's hard to find a way to reduce the -complexity. - -### Example -You'll see it when you get the warning. \ No newline at end of file diff --git a/src/docs/collapsible_else_if.txt b/src/docs/collapsible_else_if.txt deleted file mode 100644 index 4ddfca17731f..000000000000 --- a/src/docs/collapsible_else_if.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for collapsible `else { if ... }` expressions -that can be collapsed to `else if ...`. - -### Why is this bad? -Each `if`-statement adds one level of nesting, which -makes code look more complex than it really is. - -### Example -``` - -if x { - … -} else { - if y { - … - } -} -``` - -Should be written: - -``` -if x { - … -} else if y { - … -} -``` \ No newline at end of file diff --git a/src/docs/collapsible_if.txt b/src/docs/collapsible_if.txt deleted file mode 100644 index e1264ee062e9..000000000000 --- a/src/docs/collapsible_if.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for nested `if` statements which can be collapsed -by `&&`-combining their conditions. - -### Why is this bad? -Each `if`-statement adds one level of nesting, which -makes code look more complex than it really is. - -### Example -``` -if x { - if y { - // … - } -} -``` - -Use instead: -``` -if x && y { - // … -} -``` \ No newline at end of file diff --git a/src/docs/collapsible_match.txt b/src/docs/collapsible_match.txt deleted file mode 100644 index 0d59594a03a2..000000000000 --- a/src/docs/collapsible_match.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together -without adding any branches. - -Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only -cases where merging would most likely make the code more readable. - -### Why is this bad? -It is unnecessarily verbose and complex. - -### Example -``` -fn func(opt: Option>) { - let n = match opt { - Some(n) => match n { - Ok(n) => n, - _ => return, - } - None => return, - }; -} -``` -Use instead: -``` -fn func(opt: Option>) { - let n = match opt { - Some(Ok(n)) => n, - _ => return, - }; -} -``` \ No newline at end of file diff --git a/src/docs/collapsible_str_replace.txt b/src/docs/collapsible_str_replace.txt deleted file mode 100644 index c24c25a3028a..000000000000 --- a/src/docs/collapsible_str_replace.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for consecutive calls to `str::replace` (2 or more) -that can be collapsed into a single call. - -### Why is this bad? -Consecutive `str::replace` calls scan the string multiple times -with repetitive code. - -### Example -``` -let hello = "hesuo worpd" - .replace('s', "l") - .replace("u", "l") - .replace('p', "l"); -``` -Use instead: -``` -let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l"); -``` \ No newline at end of file diff --git a/src/docs/comparison_chain.txt b/src/docs/comparison_chain.txt deleted file mode 100644 index 43b09f31ff4a..000000000000 --- a/src/docs/comparison_chain.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks comparison chains written with `if` that can be -rewritten with `match` and `cmp`. - -### Why is this bad? -`if` is not guaranteed to be exhaustive and conditionals can get -repetitive - -### Known problems -The match statement may be slower due to the compiler -not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354) - -### Example -``` -fn f(x: u8, y: u8) { - if x > y { - a() - } else if x < y { - b() - } else { - c() - } -} -``` - -Use instead: -``` -use std::cmp::Ordering; -fn f(x: u8, y: u8) { - match x.cmp(&y) { - Ordering::Greater => a(), - Ordering::Less => b(), - Ordering::Equal => c() - } -} -``` \ No newline at end of file diff --git a/src/docs/comparison_to_empty.txt b/src/docs/comparison_to_empty.txt deleted file mode 100644 index db6f74fe2706..000000000000 --- a/src/docs/comparison_to_empty.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Checks for comparing to an empty slice such as `""` or `[]`, -and suggests using `.is_empty()` where applicable. - -### Why is this bad? -Some structures can answer `.is_empty()` much faster -than checking for equality. So it is good to get into the habit of using -`.is_empty()`, and having it is cheap. -Besides, it makes the intent clearer than a manual comparison in some contexts. - -### Example - -``` -if s == "" { - .. -} - -if arr == [] { - .. -} -``` -Use instead: -``` -if s.is_empty() { - .. -} - -if arr.is_empty() { - .. -} -``` \ No newline at end of file diff --git a/src/docs/copy_iterator.txt b/src/docs/copy_iterator.txt deleted file mode 100644 index 5f9a2a015b86..000000000000 --- a/src/docs/copy_iterator.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for types that implement `Copy` as well as -`Iterator`. - -### Why is this bad? -Implicit copies can be confusing when working with -iterator combinators. - -### Example -``` -#[derive(Copy, Clone)] -struct Countdown(u8); - -impl Iterator for Countdown { - // ... -} - -let a: Vec<_> = my_iterator.take(1).collect(); -let b: Vec<_> = my_iterator.collect(); -``` \ No newline at end of file diff --git a/src/docs/crate_in_macro_def.txt b/src/docs/crate_in_macro_def.txt deleted file mode 100644 index 047e986dee71..000000000000 --- a/src/docs/crate_in_macro_def.txt +++ /dev/null @@ -1,35 +0,0 @@ -### What it does -Checks for use of `crate` as opposed to `$crate` in a macro definition. - -### Why is this bad? -`crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's -crate. Rarely is the former intended. See: -https://doc.rust-lang.org/reference/macros-by-example.html#hygiene - -### Example -``` -#[macro_export] -macro_rules! print_message { - () => { - println!("{}", crate::MESSAGE); - }; -} -pub const MESSAGE: &str = "Hello!"; -``` -Use instead: -``` -#[macro_export] -macro_rules! print_message { - () => { - println!("{}", $crate::MESSAGE); - }; -} -pub const MESSAGE: &str = "Hello!"; -``` - -Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the -macro definition, e.g.: -``` -#[allow(clippy::crate_in_macro_def)] -macro_rules! ok { ... crate::foo ... } -``` \ No newline at end of file diff --git a/src/docs/create_dir.txt b/src/docs/create_dir.txt deleted file mode 100644 index e4e7937684e6..000000000000 --- a/src/docs/create_dir.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. - -### Why is this bad? -Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`. - -### Example -``` -std::fs::create_dir("foo"); -``` - -Use instead: -``` -std::fs::create_dir_all("foo"); -``` \ No newline at end of file diff --git a/src/docs/crosspointer_transmute.txt b/src/docs/crosspointer_transmute.txt deleted file mode 100644 index 49dea154970e..000000000000 --- a/src/docs/crosspointer_transmute.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for transmutes between a type `T` and `*T`. - -### Why is this bad? -It's easy to mistakenly transmute between a type and a -pointer to that type. - -### Example -``` -core::intrinsics::transmute(t) // where the result type is the same as - // `*t` or `&t`'s -``` \ No newline at end of file diff --git a/src/docs/dbg_macro.txt b/src/docs/dbg_macro.txt deleted file mode 100644 index 3e1a9a043f9f..000000000000 --- a/src/docs/dbg_macro.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of dbg!() macro. - -### Why is this bad? -`dbg!` macro is intended as a debugging tool. It -should not be in version control. - -### Example -``` -dbg!(true) -``` - -Use instead: -``` -true -``` \ No newline at end of file diff --git a/src/docs/debug_assert_with_mut_call.txt b/src/docs/debug_assert_with_mut_call.txt deleted file mode 100644 index 2c44abe1f05c..000000000000 --- a/src/docs/debug_assert_with_mut_call.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for function/method calls with a mutable -parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros. - -### Why is this bad? -In release builds `debug_assert!` macros are optimized out by the -compiler. -Therefore mutating something in a `debug_assert!` macro results in different behavior -between a release and debug build. - -### Example -``` -debug_assert_eq!(vec![3].pop(), Some(3)); - -// or - -debug_assert!(takes_a_mut_parameter(&mut x)); -``` \ No newline at end of file diff --git a/src/docs/decimal_literal_representation.txt b/src/docs/decimal_literal_representation.txt deleted file mode 100644 index daca9bbb3a84..000000000000 --- a/src/docs/decimal_literal_representation.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Warns if there is a better representation for a numeric literal. - -### Why is this bad? -Especially for big powers of 2 a hexadecimal representation is more -readable than a decimal representation. - -### Example -``` -`255` => `0xFF` -`65_535` => `0xFFFF` -`4_042_322_160` => `0xF0F0_F0F0` -``` \ No newline at end of file diff --git a/src/docs/declare_interior_mutable_const.txt b/src/docs/declare_interior_mutable_const.txt deleted file mode 100644 index 2801b5ccff80..000000000000 --- a/src/docs/declare_interior_mutable_const.txt +++ /dev/null @@ -1,46 +0,0 @@ -### What it does -Checks for declaration of `const` items which is interior -mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). - -### Why is this bad? -Consts are copied everywhere they are referenced, i.e., -every time you refer to the const a fresh instance of the `Cell` or `Mutex` -or `AtomicXxxx` will be created, which defeats the whole purpose of using -these types in the first place. - -The `const` should better be replaced by a `static` item if a global -variable is wanted, or replaced by a `const fn` if a constructor is wanted. - -### Known problems -A "non-constant" const item is a legacy way to supply an -initialized value to downstream `static` items (e.g., the -`std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, -and this lint should be suppressed. - -Even though the lint avoids triggering on a constant whose type has enums that have variants -with interior mutability, and its value uses non interior mutable variants (see -[#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and -[#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples); -it complains about associated constants without default values only based on its types; -which might not be preferable. -There're other enums plus associated constants cases that the lint cannot handle. - -Types that have underlying or potential interior mutability trigger the lint whether -the interior mutable field is used or not. See issues -[#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and - -### Example -``` -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; - -const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); -CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged -assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct -``` - -Use instead: -``` -static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15); -STATIC_ATOM.store(9, SeqCst); -assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance -``` \ No newline at end of file diff --git a/src/docs/default_instead_of_iter_empty.txt b/src/docs/default_instead_of_iter_empty.txt deleted file mode 100644 index b63ef3d18fc4..000000000000 --- a/src/docs/default_instead_of_iter_empty.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -It checks for `std::iter::Empty::default()` and suggests replacing it with -`std::iter::empty()`. -### Why is this bad? -`std::iter::empty()` is the more idiomatic way. -### Example -``` -let _ = std::iter::Empty::::default(); -let iter: std::iter::Empty = std::iter::Empty::default(); -``` -Use instead: -``` -let _ = std::iter::empty::(); -let iter: std::iter::Empty = std::iter::empty(); -``` \ No newline at end of file diff --git a/src/docs/default_numeric_fallback.txt b/src/docs/default_numeric_fallback.txt deleted file mode 100644 index 15076a0a68bf..000000000000 --- a/src/docs/default_numeric_fallback.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type -inference. - -Default numeric fallback means that if numeric types have not yet been bound to concrete -types at the end of type inference, then integer type is bound to `i32`, and similarly -floating type is bound to `f64`. - -See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback. - -### Why is this bad? -For those who are very careful about types, default numeric fallback -can be a pitfall that cause unexpected runtime behavior. - -### Known problems -This lint can only be allowed at the function level or above. - -### Example -``` -let i = 10; -let f = 1.23; -``` - -Use instead: -``` -let i = 10i32; -let f = 1.23f64; -``` \ No newline at end of file diff --git a/src/docs/default_trait_access.txt b/src/docs/default_trait_access.txt deleted file mode 100644 index e69298969c8e..000000000000 --- a/src/docs/default_trait_access.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for literal calls to `Default::default()`. - -### Why is this bad? -It's easier for the reader if the name of the type is used, rather than the -generic `Default`. - -### Example -``` -let s: String = Default::default(); -``` - -Use instead: -``` -let s = String::default(); -``` \ No newline at end of file diff --git a/src/docs/default_union_representation.txt b/src/docs/default_union_representation.txt deleted file mode 100644 index f79ff9760e57..000000000000 --- a/src/docs/default_union_representation.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Displays a warning when a union is declared with the default representation (without a `#[repr(C)]` attribute). - -### Why is this bad? -Unions in Rust have unspecified layout by default, despite many people thinking that they -lay out each field at the start of the union (like C does). That is, there are no guarantees -about the offset of the fields for unions with multiple non-ZST fields without an explicitly -specified layout. These cases may lead to undefined behavior in unsafe blocks. - -### Example -``` -union Foo { - a: i32, - b: u32, -} - -fn main() { - let _x: u32 = unsafe { - Foo { a: 0_i32 }.b // Undefined behavior: `b` is allowed to be padding - }; -} -``` -Use instead: -``` -#[repr(C)] -union Foo { - a: i32, - b: u32, -} - -fn main() { - let _x: u32 = unsafe { - Foo { a: 0_i32 }.b // Now defined behavior, this is just an i32 -> u32 transmute - }; -} -``` \ No newline at end of file diff --git a/src/docs/deprecated_cfg_attr.txt b/src/docs/deprecated_cfg_attr.txt deleted file mode 100644 index 9f264887a057..000000000000 --- a/src/docs/deprecated_cfg_attr.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it -with `#[rustfmt::skip]`. - -### Why is this bad? -Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690)) -are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes. - -### Known problems -This lint doesn't detect crate level inner attributes, because they get -processed before the PreExpansionPass lints get executed. See -[#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765) - -### Example -``` -#[cfg_attr(rustfmt, rustfmt_skip)] -fn main() { } -``` - -Use instead: -``` -#[rustfmt::skip] -fn main() { } -``` \ No newline at end of file diff --git a/src/docs/deprecated_semver.txt b/src/docs/deprecated_semver.txt deleted file mode 100644 index c9574a99b2be..000000000000 --- a/src/docs/deprecated_semver.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for `#[deprecated]` annotations with a `since` -field that is not a valid semantic version. - -### Why is this bad? -For checking the version of the deprecation, it must be -a valid semver. Failing that, the contained information is useless. - -### Example -``` -#[deprecated(since = "forever")] -fn something_else() { /* ... */ } -``` \ No newline at end of file diff --git a/src/docs/deref_addrof.txt b/src/docs/deref_addrof.txt deleted file mode 100644 index fa711b924d48..000000000000 --- a/src/docs/deref_addrof.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `*&` and `*&mut` in expressions. - -### Why is this bad? -Immediately dereferencing a reference is no-op and -makes the code less clear. - -### Known problems -Multiple dereference/addrof pairs are not handled so -the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect. - -### Example -``` -let a = f(*&mut b); -let c = *&d; -``` - -Use instead: -``` -let a = f(b); -let c = d; -``` \ No newline at end of file diff --git a/src/docs/deref_by_slicing.txt b/src/docs/deref_by_slicing.txt deleted file mode 100644 index 4dad24ac00ca..000000000000 --- a/src/docs/deref_by_slicing.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for slicing expressions which are equivalent to dereferencing the -value. - -### Why is this bad? -Some people may prefer to dereference rather than slice. - -### Example -``` -let vec = vec![1, 2, 3]; -let slice = &vec[..]; -``` -Use instead: -``` -let vec = vec![1, 2, 3]; -let slice = &*vec; -``` \ No newline at end of file diff --git a/src/docs/derivable_impls.txt b/src/docs/derivable_impls.txt deleted file mode 100644 index 5cee43956cc3..000000000000 --- a/src/docs/derivable_impls.txt +++ /dev/null @@ -1,35 +0,0 @@ -### What it does -Detects manual `std::default::Default` implementations that are identical to a derived implementation. - -### Why is this bad? -It is less concise. - -### Example -``` -struct Foo { - bar: bool -} - -impl Default for Foo { - fn default() -> Self { - Self { - bar: false - } - } -} -``` - -Use instead: -``` -#[derive(Default)] -struct Foo { - bar: bool -} -``` - -### Known problems -Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925) -in generic types and the user defined `impl` may be more generalized or -specialized than what derive will produce. This lint can't detect the manual `impl` -has exactly equal bounds, and therefore this lint is disabled for types with -generic parameters. \ No newline at end of file diff --git a/src/docs/derive_hash_xor_eq.txt b/src/docs/derive_hash_xor_eq.txt deleted file mode 100644 index fbf623d5adbc..000000000000 --- a/src/docs/derive_hash_xor_eq.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for deriving `Hash` but implementing `PartialEq` -explicitly or vice versa. - -### Why is this bad? -The implementation of these traits must agree (for -example for use with `HashMap`) so it’s probably a bad idea to use a -default-generated `Hash` implementation with an explicitly defined -`PartialEq`. In particular, the following must hold for any type: - -``` -k1 == k2 ⇒ hash(k1) == hash(k2) -``` - -### Example -``` -#[derive(Hash)] -struct Foo; - -impl PartialEq for Foo { - ... -} -``` \ No newline at end of file diff --git a/src/docs/derive_ord_xor_partial_ord.txt b/src/docs/derive_ord_xor_partial_ord.txt deleted file mode 100644 index f2107a5f69ee..000000000000 --- a/src/docs/derive_ord_xor_partial_ord.txt +++ /dev/null @@ -1,44 +0,0 @@ -### What it does -Checks for deriving `Ord` but implementing `PartialOrd` -explicitly or vice versa. - -### Why is this bad? -The implementation of these traits must agree (for -example for use with `sort`) so it’s probably a bad idea to use a -default-generated `Ord` implementation with an explicitly defined -`PartialOrd`. In particular, the following must hold for any type -implementing `Ord`: - -``` -k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap() -``` - -### Example -``` -#[derive(Ord, PartialEq, Eq)] -struct Foo; - -impl PartialOrd for Foo { - ... -} -``` -Use instead: -``` -#[derive(PartialEq, Eq)] -struct Foo; - -impl PartialOrd for Foo { - fn partial_cmp(&self, other: &Foo) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Foo { - ... -} -``` -or, if you don't need a custom ordering: -``` -#[derive(Ord, PartialOrd, PartialEq, Eq)] -struct Foo; -``` \ No newline at end of file diff --git a/src/docs/derive_partial_eq_without_eq.txt b/src/docs/derive_partial_eq_without_eq.txt deleted file mode 100644 index 932fabad666c..000000000000 --- a/src/docs/derive_partial_eq_without_eq.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for types that derive `PartialEq` and could implement `Eq`. - -### Why is this bad? -If a type `T` derives `PartialEq` and all of its members implement `Eq`, -then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used -in APIs that require `Eq` types. It also allows structs containing `T` to derive -`Eq` themselves. - -### Example -``` -#[derive(PartialEq)] -struct Foo { - i_am_eq: i32, - i_am_eq_too: Vec, -} -``` -Use instead: -``` -#[derive(PartialEq, Eq)] -struct Foo { - i_am_eq: i32, - i_am_eq_too: Vec, -} -``` \ No newline at end of file diff --git a/src/docs/disallowed_macros.txt b/src/docs/disallowed_macros.txt deleted file mode 100644 index 96fa15afabfd..000000000000 --- a/src/docs/disallowed_macros.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Denies the configured macros in clippy.toml - -Note: Even though this lint is warn-by-default, it will only trigger if -macros are defined in the clippy.toml file. - -### Why is this bad? -Some macros are undesirable in certain contexts, and it's beneficial to -lint for them as needed. - -### Example -An example clippy.toml configuration: -``` -disallowed-macros = [ - # Can use a string as the path of the disallowed macro. - "std::print", - # Can also use an inline table with a `path` key. - { path = "std::println" }, - # When using an inline table, can add a `reason` for why the macro - # is disallowed. - { path = "serde::Serialize", reason = "no serializing" }, -] -``` -``` -use serde::Serialize; - -// Example code where clippy issues a warning -println!("warns"); - -// The diagnostic will contain the message "no serializing" -#[derive(Serialize)] -struct Data { - name: String, - value: usize, -} -``` \ No newline at end of file diff --git a/src/docs/disallowed_methods.txt b/src/docs/disallowed_methods.txt deleted file mode 100644 index d8ad5b6a6674..000000000000 --- a/src/docs/disallowed_methods.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Denies the configured methods and functions in clippy.toml - -Note: Even though this lint is warn-by-default, it will only trigger if -methods are defined in the clippy.toml file. - -### Why is this bad? -Some methods are undesirable in certain contexts, and it's beneficial to -lint for them as needed. - -### Example -An example clippy.toml configuration: -``` -disallowed-methods = [ - # Can use a string as the path of the disallowed method. - "std::boxed::Box::new", - # Can also use an inline table with a `path` key. - { path = "std::time::Instant::now" }, - # When using an inline table, can add a `reason` for why the method - # is disallowed. - { path = "std::vec::Vec::leak", reason = "no leaking memory" }, -] -``` - -``` -// Example code where clippy issues a warning -let xs = vec![1, 2, 3, 4]; -xs.leak(); // Vec::leak is disallowed in the config. -// The diagnostic contains the message "no leaking memory". - -let _now = Instant::now(); // Instant::now is disallowed in the config. - -let _box = Box::new(3); // Box::new is disallowed in the config. -``` - -Use instead: -``` -// Example code which does not raise clippy warning -let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config. -xs.push(123); // Vec::push is _not_ disallowed in the config. -``` \ No newline at end of file diff --git a/src/docs/disallowed_names.txt b/src/docs/disallowed_names.txt deleted file mode 100644 index f4aaee9c77b7..000000000000 --- a/src/docs/disallowed_names.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for usage of disallowed names for variables, such -as `foo`. - -### Why is this bad? -These names are usually placeholder names and should be -avoided. - -### Example -``` -let foo = 3.14; -``` \ No newline at end of file diff --git a/src/docs/disallowed_script_idents.txt b/src/docs/disallowed_script_idents.txt deleted file mode 100644 index 2151b7a20ded..000000000000 --- a/src/docs/disallowed_script_idents.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for usage of unicode scripts other than those explicitly allowed -by the lint config. - -This lint doesn't take into account non-text scripts such as `Unknown` and `Linear_A`. -It also ignores the `Common` script type. -While configuring, be sure to use official script name [aliases] from -[the list of supported scripts][supported_scripts]. - -See also: [`non_ascii_idents`]. - -[aliases]: http://www.unicode.org/reports/tr24/tr24-31.html#Script_Value_Aliases -[supported_scripts]: https://www.unicode.org/iso15924/iso15924-codes.html - -### Why is this bad? -It may be not desired to have many different scripts for -identifiers in the codebase. - -Note that if you only want to allow plain English, you might want to use -built-in [`non_ascii_idents`] lint instead. - -[`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents - -### Example -``` -// Assuming that `clippy.toml` contains the following line: -// allowed-locales = ["Latin", "Cyrillic"] -let counter = 10; // OK, latin is allowed. -let счётчик = 10; // OK, cyrillic is allowed. -let zähler = 10; // OK, it's still latin. -let カウンタ = 10; // Will spawn the lint. -``` \ No newline at end of file diff --git a/src/docs/disallowed_types.txt b/src/docs/disallowed_types.txt deleted file mode 100644 index 2bcbcddee566..000000000000 --- a/src/docs/disallowed_types.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Denies the configured types in clippy.toml. - -Note: Even though this lint is warn-by-default, it will only trigger if -types are defined in the clippy.toml file. - -### Why is this bad? -Some types are undesirable in certain contexts. - -### Example: -An example clippy.toml configuration: -``` -disallowed-types = [ - # Can use a string as the path of the disallowed type. - "std::collections::BTreeMap", - # Can also use an inline table with a `path` key. - { path = "std::net::TcpListener" }, - # When using an inline table, can add a `reason` for why the type - # is disallowed. - { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, -] -``` - -``` -use std::collections::BTreeMap; -// or its use -let x = std::collections::BTreeMap::new(); -``` -Use instead: -``` -// A similar type that is allowed by the config -use std::collections::HashMap; -``` \ No newline at end of file diff --git a/src/docs/diverging_sub_expression.txt b/src/docs/diverging_sub_expression.txt deleted file mode 100644 index 194362218025..000000000000 --- a/src/docs/diverging_sub_expression.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for diverging calls that are not match arms or -statements. - -### Why is this bad? -It is often confusing to read. In addition, the -sub-expression evaluation order for Rust is not well documented. - -### Known problems -Someone might want to use `some_bool || panic!()` as a -shorthand. - -### Example -``` -let a = b() || panic!() || c(); -// `c()` is dead, `panic!()` is only called if `b()` returns `false` -let x = (a, b, c, panic!()); -// can simply be replaced by `panic!()` -``` \ No newline at end of file diff --git a/src/docs/doc_link_with_quotes.txt b/src/docs/doc_link_with_quotes.txt deleted file mode 100644 index 107c8ac116d9..000000000000 --- a/src/docs/doc_link_with_quotes.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) -outside of code blocks -### Why is this bad? -It is likely a typo when defining an intra-doc link - -### Example -``` -/// See also: ['foo'] -fn bar() {} -``` -Use instead: -``` -/// See also: [`foo`] -fn bar() {} -``` \ No newline at end of file diff --git a/src/docs/doc_markdown.txt b/src/docs/doc_markdown.txt deleted file mode 100644 index 94f54c587e30..000000000000 --- a/src/docs/doc_markdown.txt +++ /dev/null @@ -1,35 +0,0 @@ -### What it does -Checks for the presence of `_`, `::` or camel-case words -outside ticks in documentation. - -### Why is this bad? -*Rustdoc* supports markdown formatting, `_`, `::` and -camel-case probably indicates some code which should be included between -ticks. `_` can also be used for emphasis in markdown, this lint tries to -consider that. - -### Known problems -Lots of bad docs won’t be fixed, what the lint checks -for is limited, and there are still false positives. HTML elements and their -content are not linted. - -In addition, when writing documentation comments, including `[]` brackets -inside a link text would trip the parser. Therefore, documenting link with -`[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec -would fail. - -### Examples -``` -/// Do something with the foo_bar parameter. See also -/// that::other::module::foo. -// ^ `foo_bar` and `that::other::module::foo` should be ticked. -fn doit(foo_bar: usize) {} -``` - -``` -// Link text with `[]` brackets should be written as following: -/// Consume the array and return the inner -/// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec]. -/// [SmallVec]: SmallVec -fn main() {} -``` \ No newline at end of file diff --git a/src/docs/double_comparisons.txt b/src/docs/double_comparisons.txt deleted file mode 100644 index 7dc6818779f4..000000000000 --- a/src/docs/double_comparisons.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for double comparisons that could be simplified to a single expression. - - -### Why is this bad? -Readability. - -### Example -``` -if x == y || x < y {} -``` - -Use instead: - -``` -if x <= y {} -``` \ No newline at end of file diff --git a/src/docs/double_must_use.txt b/src/docs/double_must_use.txt deleted file mode 100644 index 0017d10d40d3..000000000000 --- a/src/docs/double_must_use.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for a `#[must_use]` attribute without -further information on functions and methods that return a type already -marked as `#[must_use]`. - -### Why is this bad? -The attribute isn't needed. Not using the result -will already be reported. Alternatively, one can add some text to the -attribute to improve the lint message. - -### Examples -``` -#[must_use] -fn double_must_use() -> Result<(), ()> { - unimplemented!(); -} -``` \ No newline at end of file diff --git a/src/docs/double_neg.txt b/src/docs/double_neg.txt deleted file mode 100644 index a07f67496d7c..000000000000 --- a/src/docs/double_neg.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Detects expressions of the form `--x`. - -### Why is this bad? -It can mislead C/C++ programmers to think `x` was -decremented. - -### Example -``` -let mut x = 3; ---x; -``` \ No newline at end of file diff --git a/src/docs/double_parens.txt b/src/docs/double_parens.txt deleted file mode 100644 index 260d7dd575e5..000000000000 --- a/src/docs/double_parens.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for unnecessary double parentheses. - -### Why is this bad? -This makes code harder to read and might indicate a -mistake. - -### Example -``` -fn simple_double_parens() -> i32 { - ((0)) -} - -foo((0)); -``` - -Use instead: -``` -fn simple_no_parens() -> i32 { - 0 -} - -foo(0); -``` \ No newline at end of file diff --git a/src/docs/drop_copy.txt b/src/docs/drop_copy.txt deleted file mode 100644 index f917ca8ed21a..000000000000 --- a/src/docs/drop_copy.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for calls to `std::mem::drop` with a value -that derives the Copy trait - -### Why is this bad? -Calling `std::mem::drop` [does nothing for types that -implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the -value will be copied and moved into the function on invocation. - -### Example -``` -let x: i32 = 42; // i32 implements Copy -std::mem::drop(x) // A copy of x is passed to the function, leaving the - // original unaffected -``` \ No newline at end of file diff --git a/src/docs/drop_non_drop.txt b/src/docs/drop_non_drop.txt deleted file mode 100644 index ee1e3a6c216e..000000000000 --- a/src/docs/drop_non_drop.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for calls to `std::mem::drop` with a value that does not implement `Drop`. - -### Why is this bad? -Calling `std::mem::drop` is no different than dropping such a type. A different value may -have been intended. - -### Example -``` -struct Foo; -let x = Foo; -std::mem::drop(x); -``` \ No newline at end of file diff --git a/src/docs/drop_ref.txt b/src/docs/drop_ref.txt deleted file mode 100644 index c4f7adf0cfa3..000000000000 --- a/src/docs/drop_ref.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for calls to `std::mem::drop` with a reference -instead of an owned value. - -### Why is this bad? -Calling `drop` on a reference will only drop the -reference itself, which is a no-op. It will not call the `drop` method (from -the `Drop` trait implementation) on the underlying referenced value, which -is likely what was intended. - -### Example -``` -let mut lock_guard = mutex.lock(); -std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex -// still locked -operation_that_requires_mutex_to_be_unlocked(); -``` \ No newline at end of file diff --git a/src/docs/duplicate_mod.txt b/src/docs/duplicate_mod.txt deleted file mode 100644 index 709a9aba03ad..000000000000 --- a/src/docs/duplicate_mod.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Checks for files that are included as modules multiple times. - -### Why is this bad? -Loading a file as a module more than once causes it to be compiled -multiple times, taking longer and putting duplicate content into the -module tree. - -### Example -``` -// lib.rs -mod a; -mod b; -``` -``` -// a.rs -#[path = "./b.rs"] -mod b; -``` - -Use instead: - -``` -// lib.rs -mod a; -mod b; -``` -``` -// a.rs -use crate::b; -``` \ No newline at end of file diff --git a/src/docs/duplicate_underscore_argument.txt b/src/docs/duplicate_underscore_argument.txt deleted file mode 100644 index a8fcd6a9fbe6..000000000000 --- a/src/docs/duplicate_underscore_argument.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for function arguments having the similar names -differing by an underscore. - -### Why is this bad? -It affects code readability. - -### Example -``` -fn foo(a: i32, _a: i32) {} -``` - -Use instead: -``` -fn bar(a: i32, _b: i32) {} -``` \ No newline at end of file diff --git a/src/docs/duration_subsec.txt b/src/docs/duration_subsec.txt deleted file mode 100644 index e7e0ca88745e..000000000000 --- a/src/docs/duration_subsec.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for calculation of subsecond microseconds or milliseconds -from other `Duration` methods. - -### Why is this bad? -It's more concise to call `Duration::subsec_micros()` or -`Duration::subsec_millis()` than to calculate them. - -### Example -``` -let micros = duration.subsec_nanos() / 1_000; -let millis = duration.subsec_nanos() / 1_000_000; -``` - -Use instead: -``` -let micros = duration.subsec_micros(); -let millis = duration.subsec_millis(); -``` \ No newline at end of file diff --git a/src/docs/else_if_without_else.txt b/src/docs/else_if_without_else.txt deleted file mode 100644 index 33f5d0f91859..000000000000 --- a/src/docs/else_if_without_else.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for usage of if expressions with an `else if` branch, -but without a final `else` branch. - -### Why is this bad? -Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10). - -### Example -``` -if x.is_positive() { - a(); -} else if x.is_negative() { - b(); -} -``` - -Use instead: - -``` -if x.is_positive() { - a(); -} else if x.is_negative() { - b(); -} else { - // We don't care about zero. -} -``` \ No newline at end of file diff --git a/src/docs/empty_drop.txt b/src/docs/empty_drop.txt deleted file mode 100644 index d0c0c24a9c88..000000000000 --- a/src/docs/empty_drop.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for empty `Drop` implementations. - -### Why is this bad? -Empty `Drop` implementations have no effect when dropping an instance of the type. They are -most likely useless. However, an empty `Drop` implementation prevents a type from being -destructured, which might be the intention behind adding the implementation as a marker. - -### Example -``` -struct S; - -impl Drop for S { - fn drop(&mut self) {} -} -``` -Use instead: -``` -struct S; -``` \ No newline at end of file diff --git a/src/docs/empty_enum.txt b/src/docs/empty_enum.txt deleted file mode 100644 index f7b41c41ee5a..000000000000 --- a/src/docs/empty_enum.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for `enum`s with no variants. - -As of this writing, the `never_type` is still a -nightly-only experimental API. Therefore, this lint is only triggered -if the `never_type` is enabled. - -### Why is this bad? -If you want to introduce a type which -can't be instantiated, you should use `!` (the primitive type "never"), -or a wrapper around it, because `!` has more extensive -compiler support (type inference, etc...) and wrappers -around it are the conventional way to define an uninhabited type. -For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html) - - -### Example -``` -enum Test {} -``` - -Use instead: -``` -#![feature(never_type)] - -struct Test(!); -``` \ No newline at end of file diff --git a/src/docs/empty_line_after_outer_attr.txt b/src/docs/empty_line_after_outer_attr.txt deleted file mode 100644 index c85242bbee0e..000000000000 --- a/src/docs/empty_line_after_outer_attr.txt +++ /dev/null @@ -1,35 +0,0 @@ -### What it does -Checks for empty lines after outer attributes - -### Why is this bad? -Most likely the attribute was meant to be an inner attribute using a '!'. -If it was meant to be an outer attribute, then the following item -should not be separated by empty lines. - -### Known problems -Can cause false positives. - -From the clippy side it's difficult to detect empty lines between an attributes and the -following item because empty lines and comments are not part of the AST. The parsing -currently works for basic cases but is not perfect. - -### Example -``` -#[allow(dead_code)] - -fn not_quite_good_code() { } -``` - -Use instead: -``` -// Good (as inner attribute) -#![allow(dead_code)] - -fn this_is_fine() { } - -// or - -// Good (as outer attribute) -#[allow(dead_code)] -fn this_is_fine_too() { } -``` \ No newline at end of file diff --git a/src/docs/empty_loop.txt b/src/docs/empty_loop.txt deleted file mode 100644 index fea49a74d04e..000000000000 --- a/src/docs/empty_loop.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for empty `loop` expressions. - -### Why is this bad? -These busy loops burn CPU cycles without doing -anything. It is _almost always_ a better idea to `panic!` than to have -a busy loop. - -If panicking isn't possible, think of the environment and either: - - block on something - - sleep the thread for some microseconds - - yield or pause the thread - -For `std` targets, this can be done with -[`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html) -or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html). - -For `no_std` targets, doing this is more complicated, especially because -`#[panic_handler]`s can't panic. To stop/pause the thread, you will -probably need to invoke some target-specific intrinsic. Examples include: - - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html) - - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html) - -### Example -``` -loop {} -``` \ No newline at end of file diff --git a/src/docs/empty_structs_with_brackets.txt b/src/docs/empty_structs_with_brackets.txt deleted file mode 100644 index ab5e35ae2ada..000000000000 --- a/src/docs/empty_structs_with_brackets.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Finds structs without fields (a so-called "empty struct") that are declared with brackets. - -### Why is this bad? -Empty brackets after a struct declaration can be omitted. - -### Example -``` -struct Cookie {} -``` -Use instead: -``` -struct Cookie; -``` \ No newline at end of file diff --git a/src/docs/enum_clike_unportable_variant.txt b/src/docs/enum_clike_unportable_variant.txt deleted file mode 100644 index d30a973a5a13..000000000000 --- a/src/docs/enum_clike_unportable_variant.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for C-like enumerations that are -`repr(isize/usize)` and have values that don't fit into an `i32`. - -### Why is this bad? -This will truncate the variant value on 32 bit -architectures, but works fine on 64 bit. - -### Example -``` -#[repr(usize)] -enum NonPortable { - X = 0x1_0000_0000, - Y = 0, -} -``` \ No newline at end of file diff --git a/src/docs/enum_glob_use.txt b/src/docs/enum_glob_use.txt deleted file mode 100644 index 3776822c35b0..000000000000 --- a/src/docs/enum_glob_use.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for `use Enum::*`. - -### Why is this bad? -It is usually better style to use the prefixed name of -an enumeration variant, rather than importing variants. - -### Known problems -Old-style enumerations that prefix the variants are -still around. - -### Example -``` -use std::cmp::Ordering::*; - -foo(Less); -``` - -Use instead: -``` -use std::cmp::Ordering; - -foo(Ordering::Less) -``` \ No newline at end of file diff --git a/src/docs/enum_variant_names.txt b/src/docs/enum_variant_names.txt deleted file mode 100644 index e726925edda8..000000000000 --- a/src/docs/enum_variant_names.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Detects enumeration variants that are prefixed or suffixed -by the same characters. - -### Why is this bad? -Enumeration variant names should specify their variant, -not repeat the enumeration name. - -### Limitations -Characters with no casing will be considered when comparing prefixes/suffixes -This applies to numbers and non-ascii characters without casing -e.g. `Foo1` and `Foo2` is considered to have different prefixes -(the prefixes are `Foo1` and `Foo2` respectively), as also `Bar螃`, `Bar蟹` - -### Example -``` -enum Cake { - BlackForestCake, - HummingbirdCake, - BattenbergCake, -} -``` -Use instead: -``` -enum Cake { - BlackForest, - Hummingbird, - Battenberg, -} -``` \ No newline at end of file diff --git a/src/docs/eq_op.txt b/src/docs/eq_op.txt deleted file mode 100644 index 2d75a0ec546e..000000000000 --- a/src/docs/eq_op.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for equal operands to comparison, logical and -bitwise, difference and division binary operators (`==`, `>`, etc., `&&`, -`||`, `&`, `|`, `^`, `-` and `/`). - -### Why is this bad? -This is usually just a typo or a copy and paste error. - -### Known problems -False negatives: We had some false positives regarding -calls (notably [racer](https://github.com/phildawes/racer) had one instance -of `x.pop() && x.pop()`), so we removed matching any function or method -calls. We may introduce a list of known pure functions in the future. - -### Example -``` -if x + 1 == x + 1 {} - -// or - -assert_eq!(a, a); -``` \ No newline at end of file diff --git a/src/docs/equatable_if_let.txt b/src/docs/equatable_if_let.txt deleted file mode 100644 index 9997046954c2..000000000000 --- a/src/docs/equatable_if_let.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for pattern matchings that can be expressed using equality. - -### Why is this bad? - -* It reads better and has less cognitive load because equality won't cause binding. -* It is a [Yoda condition](https://en.wikipedia.org/wiki/Yoda_conditions). Yoda conditions are widely -criticized for increasing the cognitive load of reading the code. -* Equality is a simple bool expression and can be merged with `&&` and `||` and -reuse if blocks - -### Example -``` -if let Some(2) = x { - do_thing(); -} -``` -Use instead: -``` -if x == Some(2) { - do_thing(); -} -``` \ No newline at end of file diff --git a/src/docs/erasing_op.txt b/src/docs/erasing_op.txt deleted file mode 100644 index 3d285a6d86e4..000000000000 --- a/src/docs/erasing_op.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for erasing operations, e.g., `x * 0`. - -### Why is this bad? -The whole expression can be replaced by zero. -This is most likely not the intended outcome and should probably be -corrected - -### Example -``` -let x = 1; -0 / x; -0 * x; -x & 0; -``` \ No newline at end of file diff --git a/src/docs/err_expect.txt b/src/docs/err_expect.txt deleted file mode 100644 index 1dc83c5ce0ee..000000000000 --- a/src/docs/err_expect.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for `.err().expect()` calls on the `Result` type. - -### Why is this bad? -`.expect_err()` can be called directly to avoid the extra type conversion from `err()`. - -### Example -``` -let x: Result = Ok(10); -x.err().expect("Testing err().expect()"); -``` -Use instead: -``` -let x: Result = Ok(10); -x.expect_err("Testing expect_err"); -``` \ No newline at end of file diff --git a/src/docs/excessive_precision.txt b/src/docs/excessive_precision.txt deleted file mode 100644 index 517879c47152..000000000000 --- a/src/docs/excessive_precision.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for float literals with a precision greater -than that supported by the underlying type. - -### Why is this bad? -Rust will truncate the literal silently. - -### Example -``` -let v: f32 = 0.123_456_789_9; -println!("{}", v); // 0.123_456_789 -``` - -Use instead: -``` -let v: f64 = 0.123_456_789_9; -println!("{}", v); // 0.123_456_789_9 -``` \ No newline at end of file diff --git a/src/docs/exhaustive_enums.txt b/src/docs/exhaustive_enums.txt deleted file mode 100644 index d1032a7a29aa..000000000000 --- a/src/docs/exhaustive_enums.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` - -### Why is this bad? -Exhaustive enums are typically fine, but a project which does -not wish to make a stability commitment around exported enums may wish to -disable them by default. - -### Example -``` -enum Foo { - Bar, - Baz -} -``` -Use instead: -``` -#[non_exhaustive] -enum Foo { - Bar, - Baz -} -``` \ No newline at end of file diff --git a/src/docs/exhaustive_structs.txt b/src/docs/exhaustive_structs.txt deleted file mode 100644 index fd6e4f5caf1f..000000000000 --- a/src/docs/exhaustive_structs.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Warns on any exported `structs`s that are not tagged `#[non_exhaustive]` - -### Why is this bad? -Exhaustive structs are typically fine, but a project which does -not wish to make a stability commitment around exported structs may wish to -disable them by default. - -### Example -``` -struct Foo { - bar: u8, - baz: String, -} -``` -Use instead: -``` -#[non_exhaustive] -struct Foo { - bar: u8, - baz: String, -} -``` \ No newline at end of file diff --git a/src/docs/exit.txt b/src/docs/exit.txt deleted file mode 100644 index 1e6154d43e05..000000000000 --- a/src/docs/exit.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -`exit()` terminates the program and doesn't provide a -stack trace. - -### Why is this bad? -Ideally a program is terminated by finishing -the main function. - -### Example -``` -std::process::exit(0) -``` \ No newline at end of file diff --git a/src/docs/expect_fun_call.txt b/src/docs/expect_fun_call.txt deleted file mode 100644 index d82d9aa9baff..000000000000 --- a/src/docs/expect_fun_call.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, -etc., and suggests to use `unwrap_or_else` instead - -### Why is this bad? -The function will always be called. - -### Known problems -If the function has side-effects, not calling it will -change the semantics of the program, but you shouldn't rely on that anyway. - -### Example -``` -foo.expect(&format!("Err {}: {}", err_code, err_msg)); - -// or - -foo.expect(format!("Err {}: {}", err_code, err_msg).as_str()); -``` - -Use instead: -``` -foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg)); -``` \ No newline at end of file diff --git a/src/docs/expect_used.txt b/src/docs/expect_used.txt deleted file mode 100644 index 4a6981e334fd..000000000000 --- a/src/docs/expect_used.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s. - -### Why is this bad? -Usually it is better to handle the `None` or `Err` case. -Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why -this lint is `Allow` by default. - -`result.expect()` will let the thread panic on `Err` -values. Normally, you want to implement more sophisticated error handling, -and propagate errors upwards with `?` operator. - -### Examples -``` -option.expect("one"); -result.expect("one"); -``` - -Use instead: -``` -option?; - -// or - -result?; -``` \ No newline at end of file diff --git a/src/docs/expl_impl_clone_on_copy.txt b/src/docs/expl_impl_clone_on_copy.txt deleted file mode 100644 index 391d93b6713c..000000000000 --- a/src/docs/expl_impl_clone_on_copy.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for explicit `Clone` implementations for `Copy` -types. - -### Why is this bad? -To avoid surprising behavior, these traits should -agree and the behavior of `Copy` cannot be overridden. In almost all -situations a `Copy` type should have a `Clone` implementation that does -nothing more than copy the object, which is what `#[derive(Copy, Clone)]` -gets you. - -### Example -``` -#[derive(Copy)] -struct Foo; - -impl Clone for Foo { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/explicit_auto_deref.txt b/src/docs/explicit_auto_deref.txt deleted file mode 100644 index 65b256317725..000000000000 --- a/src/docs/explicit_auto_deref.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for dereferencing expressions which would be covered by auto-deref. - -### Why is this bad? -This unnecessarily complicates the code. - -### Example -``` -let x = String::new(); -let y: &str = &*x; -``` -Use instead: -``` -let x = String::new(); -let y: &str = &x; -``` \ No newline at end of file diff --git a/src/docs/explicit_counter_loop.txt b/src/docs/explicit_counter_loop.txt deleted file mode 100644 index 2661a43e1034..000000000000 --- a/src/docs/explicit_counter_loop.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks `for` loops over slices with an explicit counter -and suggests the use of `.enumerate()`. - -### Why is this bad? -Using `.enumerate()` makes the intent more clear, -declutters the code and may be faster in some instances. - -### Example -``` -let mut i = 0; -for item in &v { - bar(i, *item); - i += 1; -} -``` - -Use instead: -``` -for (i, item) in v.iter().enumerate() { bar(i, *item); } -``` \ No newline at end of file diff --git a/src/docs/explicit_deref_methods.txt b/src/docs/explicit_deref_methods.txt deleted file mode 100644 index e14e981c7073..000000000000 --- a/src/docs/explicit_deref_methods.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for explicit `deref()` or `deref_mut()` method calls. - -### Why is this bad? -Dereferencing by `&*x` or `&mut *x` is clearer and more concise, -when not part of a method chain. - -### Example -``` -use std::ops::Deref; -let a: &mut String = &mut String::from("foo"); -let b: &str = a.deref(); -``` - -Use instead: -``` -let a: &mut String = &mut String::from("foo"); -let b = &*a; -``` - -This lint excludes: -``` -let _ = d.unwrap().deref(); -``` \ No newline at end of file diff --git a/src/docs/explicit_into_iter_loop.txt b/src/docs/explicit_into_iter_loop.txt deleted file mode 100644 index 3931dfd69a31..000000000000 --- a/src/docs/explicit_into_iter_loop.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for loops on `y.into_iter()` where `y` will do, and -suggests the latter. - -### Why is this bad? -Readability. - -### Example -``` -// with `y` a `Vec` or slice: -for x in y.into_iter() { - // .. -} -``` -can be rewritten to -``` -for x in y { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/explicit_iter_loop.txt b/src/docs/explicit_iter_loop.txt deleted file mode 100644 index cabe72e91d04..000000000000 --- a/src/docs/explicit_iter_loop.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for loops on `x.iter()` where `&x` will do, and -suggests the latter. - -### Why is this bad? -Readability. - -### Known problems -False negatives. We currently only warn on some known -types. - -### Example -``` -// with `y` a `Vec` or slice: -for x in y.iter() { - // .. -} -``` - -Use instead: -``` -for x in &y { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/explicit_write.txt b/src/docs/explicit_write.txt deleted file mode 100644 index eafed5d39e5c..000000000000 --- a/src/docs/explicit_write.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for usage of `write!()` / `writeln()!` which can be -replaced with `(e)print!()` / `(e)println!()` - -### Why is this bad? -Using `(e)println! is clearer and more concise - -### Example -``` -writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap(); -writeln!(&mut std::io::stdout(), "foo: {:?}", bar).unwrap(); -``` - -Use instead: -``` -eprintln!("foo: {:?}", bar); -println!("foo: {:?}", bar); -``` \ No newline at end of file diff --git a/src/docs/extend_with_drain.txt b/src/docs/extend_with_drain.txt deleted file mode 100644 index 2f31dcf5f740..000000000000 --- a/src/docs/extend_with_drain.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for occurrences where one vector gets extended instead of append - -### Why is this bad? -Using `append` instead of `extend` is more concise and faster - -### Example -``` -let mut a = vec![1, 2, 3]; -let mut b = vec![4, 5, 6]; - -a.extend(b.drain(..)); -``` - -Use instead: -``` -let mut a = vec![1, 2, 3]; -let mut b = vec![4, 5, 6]; - -a.append(&mut b); -``` \ No newline at end of file diff --git a/src/docs/extra_unused_lifetimes.txt b/src/docs/extra_unused_lifetimes.txt deleted file mode 100644 index bc1814aa4752..000000000000 --- a/src/docs/extra_unused_lifetimes.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for lifetimes in generics that are never used -anywhere else. - -### Why is this bad? -The additional lifetimes make the code look more -complicated, while there is nothing out of the ordinary going on. Removing -them leads to more readable code. - -### Example -``` -// unnecessary lifetimes -fn unused_lifetime<'a>(x: u8) { - // .. -} -``` - -Use instead: -``` -fn no_lifetime(x: u8) { - // ... -} -``` \ No newline at end of file diff --git a/src/docs/fallible_impl_from.txt b/src/docs/fallible_impl_from.txt deleted file mode 100644 index 588a5bb103d4..000000000000 --- a/src/docs/fallible_impl_from.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for impls of `From<..>` that contain `panic!()` or `unwrap()` - -### Why is this bad? -`TryFrom` should be used if there's a possibility of failure. - -### Example -``` -struct Foo(i32); - -impl From for Foo { - fn from(s: String) -> Self { - Foo(s.parse().unwrap()) - } -} -``` - -Use instead: -``` -struct Foo(i32); - -impl TryFrom for Foo { - type Error = (); - fn try_from(s: String) -> Result { - if let Ok(parsed) = s.parse() { - Ok(Foo(parsed)) - } else { - Err(()) - } - } -} -``` \ No newline at end of file diff --git a/src/docs/field_reassign_with_default.txt b/src/docs/field_reassign_with_default.txt deleted file mode 100644 index e58b7239fde9..000000000000 --- a/src/docs/field_reassign_with_default.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for immediate reassignment of fields initialized -with Default::default(). - -### Why is this bad? -It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax). - -### Known problems -Assignments to patterns that are of tuple type are not linted. - -### Example -``` -let mut a: A = Default::default(); -a.i = 42; -``` - -Use instead: -``` -let a = A { - i: 42, - .. Default::default() -}; -``` \ No newline at end of file diff --git a/src/docs/filetype_is_file.txt b/src/docs/filetype_is_file.txt deleted file mode 100644 index ad14bd62c4de..000000000000 --- a/src/docs/filetype_is_file.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for `FileType::is_file()`. - -### Why is this bad? -When people testing a file type with `FileType::is_file` -they are testing whether a path is something they can get bytes from. But -`is_file` doesn't cover special file types in unix-like systems, and doesn't cover -symlink in windows. Using `!FileType::is_dir()` is a better way to that intention. - -### Example -``` -let metadata = std::fs::metadata("foo.txt")?; -let filetype = metadata.file_type(); - -if filetype.is_file() { - // read file -} -``` - -should be written as: - -``` -let metadata = std::fs::metadata("foo.txt")?; -let filetype = metadata.file_type(); - -if !filetype.is_dir() { - // read file -} -``` \ No newline at end of file diff --git a/src/docs/filter_map_identity.txt b/src/docs/filter_map_identity.txt deleted file mode 100644 index 83b666f2e278..000000000000 --- a/src/docs/filter_map_identity.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for usage of `filter_map(|x| x)`. - -### Why is this bad? -Readability, this can be written more concisely by using `flatten`. - -### Example -``` -iter.filter_map(|x| x); -``` -Use instead: -``` -iter.flatten(); -``` \ No newline at end of file diff --git a/src/docs/filter_map_next.txt b/src/docs/filter_map_next.txt deleted file mode 100644 index b38620b56a50..000000000000 --- a/src/docs/filter_map_next.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `_.filter_map(_).next()`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.find_map(_)`. - -### Example -``` - (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next(); -``` -Can be written as - -``` - (0..3).find_map(|x| if x == 2 { Some(x) } else { None }); -``` \ No newline at end of file diff --git a/src/docs/filter_next.txt b/src/docs/filter_next.txt deleted file mode 100644 index 898a74166dc1..000000000000 --- a/src/docs/filter_next.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `_.filter(_).next()`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.find(_)`. - -### Example -``` -vec.iter().filter(|x| **x == 0).next(); -``` - -Use instead: -``` -vec.iter().find(|x| **x == 0); -``` \ No newline at end of file diff --git a/src/docs/flat_map_identity.txt b/src/docs/flat_map_identity.txt deleted file mode 100644 index a5ee79b4982f..000000000000 --- a/src/docs/flat_map_identity.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for usage of `flat_map(|x| x)`. - -### Why is this bad? -Readability, this can be written more concisely by using `flatten`. - -### Example -``` -iter.flat_map(|x| x); -``` -Can be written as -``` -iter.flatten(); -``` \ No newline at end of file diff --git a/src/docs/flat_map_option.txt b/src/docs/flat_map_option.txt deleted file mode 100644 index d50b9156d365..000000000000 --- a/src/docs/flat_map_option.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usages of `Iterator::flat_map()` where `filter_map()` could be -used instead. - -### Why is this bad? -When applicable, `filter_map()` is more clear since it shows that -`Option` is used to produce 0 or 1 items. - -### Example -``` -let nums: Vec = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect(); -``` -Use instead: -``` -let nums: Vec = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect(); -``` \ No newline at end of file diff --git a/src/docs/float_arithmetic.txt b/src/docs/float_arithmetic.txt deleted file mode 100644 index 1f9bce5abd59..000000000000 --- a/src/docs/float_arithmetic.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for float arithmetic. - -### Why is this bad? -For some embedded systems or kernel development, it -can be useful to rule out floating-point numbers. - -### Example -``` -a + 1.0; -``` \ No newline at end of file diff --git a/src/docs/float_cmp.txt b/src/docs/float_cmp.txt deleted file mode 100644 index c19907c903e9..000000000000 --- a/src/docs/float_cmp.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for (in-)equality comparisons on floating-point -values (apart from zero), except in functions called `*eq*` (which probably -implement equality for a type involving floats). - -### Why is this bad? -Floating point calculations are usually imprecise, so -asking if two values are *exactly* equal is asking for trouble. For a good -guide on what to do, see [the floating point -guide](http://www.floating-point-gui.de/errors/comparison). - -### Example -``` -let x = 1.2331f64; -let y = 1.2332f64; - -if y == 1.23f64 { } -if y != x {} // where both are floats -``` - -Use instead: -``` -let error_margin = f64::EPSILON; // Use an epsilon for comparison -// Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. -// let error_margin = std::f64::EPSILON; -if (y - 1.23f64).abs() < error_margin { } -if (y - x).abs() > error_margin { } -``` \ No newline at end of file diff --git a/src/docs/float_cmp_const.txt b/src/docs/float_cmp_const.txt deleted file mode 100644 index 9208feaacd81..000000000000 --- a/src/docs/float_cmp_const.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for (in-)equality comparisons on floating-point -value and constant, except in functions called `*eq*` (which probably -implement equality for a type involving floats). - -### Why is this bad? -Floating point calculations are usually imprecise, so -asking if two values are *exactly* equal is asking for trouble. For a good -guide on what to do, see [the floating point -guide](http://www.floating-point-gui.de/errors/comparison). - -### Example -``` -let x: f64 = 1.0; -const ONE: f64 = 1.00; - -if x == ONE { } // where both are floats -``` - -Use instead: -``` -let error_margin = f64::EPSILON; // Use an epsilon for comparison -// Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. -// let error_margin = std::f64::EPSILON; -if (x - ONE).abs() < error_margin { } -``` \ No newline at end of file diff --git a/src/docs/float_equality_without_abs.txt b/src/docs/float_equality_without_abs.txt deleted file mode 100644 index 556b574e15d3..000000000000 --- a/src/docs/float_equality_without_abs.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for statements of the form `(a - b) < f32::EPSILON` or -`(a - b) < f64::EPSILON`. Notes the missing `.abs()`. - -### Why is this bad? -The code without `.abs()` is more likely to have a bug. - -### Known problems -If the user can ensure that b is larger than a, the `.abs()` is -technically unnecessary. However, it will make the code more robust and doesn't have any -large performance implications. If the abs call was deliberately left out for performance -reasons, it is probably better to state this explicitly in the code, which then can be done -with an allow. - -### Example -``` -pub fn is_roughly_equal(a: f32, b: f32) -> bool { - (a - b) < f32::EPSILON -} -``` -Use instead: -``` -pub fn is_roughly_equal(a: f32, b: f32) -> bool { - (a - b).abs() < f32::EPSILON -} -``` \ No newline at end of file diff --git a/src/docs/fn_address_comparisons.txt b/src/docs/fn_address_comparisons.txt deleted file mode 100644 index 7d2b7b681deb..000000000000 --- a/src/docs/fn_address_comparisons.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for comparisons with an address of a function item. - -### Why is this bad? -Function item address is not guaranteed to be unique and could vary -between different code generation units. Furthermore different function items could have -the same address after being merged together. - -### Example -``` -type F = fn(); -fn a() {} -let f: F = a; -if f == a { - // ... -} -``` \ No newline at end of file diff --git a/src/docs/fn_params_excessive_bools.txt b/src/docs/fn_params_excessive_bools.txt deleted file mode 100644 index 2eae0563368c..000000000000 --- a/src/docs/fn_params_excessive_bools.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Checks for excessive use of -bools in function definitions. - -### Why is this bad? -Calls to such functions -are confusing and error prone, because it's -hard to remember argument order and you have -no type system support to back you up. Using -two-variant enums instead of bools often makes -API easier to use. - -### Example -``` -fn f(is_round: bool, is_hot: bool) { ... } -``` - -Use instead: -``` -enum Shape { - Round, - Spiky, -} - -enum Temperature { - Hot, - IceCold, -} - -fn f(shape: Shape, temperature: Temperature) { ... } -``` \ No newline at end of file diff --git a/src/docs/fn_to_numeric_cast.txt b/src/docs/fn_to_numeric_cast.txt deleted file mode 100644 index 1f587f6d7176..000000000000 --- a/src/docs/fn_to_numeric_cast.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for casts of function pointers to something other than usize - -### Why is this bad? -Casting a function pointer to anything other than usize/isize is not portable across -architectures, because you end up losing bits if the target type is too small or end up with a -bunch of extra bits that waste space and add more instructions to the final binary than -strictly necessary for the problem - -Casting to isize also doesn't make sense since there are no signed addresses. - -### Example -``` -fn fun() -> i32 { 1 } -let _ = fun as i64; -``` - -Use instead: -``` -let _ = fun as usize; -``` \ No newline at end of file diff --git a/src/docs/fn_to_numeric_cast_any.txt b/src/docs/fn_to_numeric_cast_any.txt deleted file mode 100644 index ee3c33d23725..000000000000 --- a/src/docs/fn_to_numeric_cast_any.txt +++ /dev/null @@ -1,35 +0,0 @@ -### What it does -Checks for casts of a function pointer to any integer type. - -### Why is this bad? -Casting a function pointer to an integer can have surprising results and can occur -accidentally if parentheses are omitted from a function call. If you aren't doing anything -low-level with function pointers then you can opt-out of casting functions to integers in -order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function -pointer casts in your code. - -### Example -``` -// fn1 is cast as `usize` -fn fn1() -> u16 { - 1 -}; -let _ = fn1 as usize; -``` - -Use instead: -``` -// maybe you intended to call the function? -fn fn2() -> u16 { - 1 -}; -let _ = fn2() as usize; - -// or - -// maybe you intended to cast it to a function type? -fn fn3() -> u16 { - 1 -} -let _ = fn3 as fn() -> u16; -``` \ No newline at end of file diff --git a/src/docs/fn_to_numeric_cast_with_truncation.txt b/src/docs/fn_to_numeric_cast_with_truncation.txt deleted file mode 100644 index 69f12fa319f1..000000000000 --- a/src/docs/fn_to_numeric_cast_with_truncation.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for casts of a function pointer to a numeric type not wide enough to -store address. - -### Why is this bad? -Such a cast discards some bits of the function's address. If this is intended, it would be more -clearly expressed by casting to usize first, then casting the usize to the intended type (with -a comment) to perform the truncation. - -### Example -``` -fn fn1() -> i16 { - 1 -}; -let _ = fn1 as i32; -``` - -Use instead: -``` -// Cast to usize first, then comment with the reason for the truncation -fn fn1() -> i16 { - 1 -}; -let fn_ptr = fn1 as usize; -let fn_ptr_truncated = fn_ptr as i32; -``` \ No newline at end of file diff --git a/src/docs/for_kv_map.txt b/src/docs/for_kv_map.txt deleted file mode 100644 index a9a2ffee9c74..000000000000 --- a/src/docs/for_kv_map.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for iterating a map (`HashMap` or `BTreeMap`) and -ignoring either the keys or values. - -### Why is this bad? -Readability. There are `keys` and `values` methods that -can be used to express that don't need the values or keys. - -### Example -``` -for (k, _) in &map { - .. -} -``` - -could be replaced by - -``` -for k in map.keys() { - .. -} -``` \ No newline at end of file diff --git a/src/docs/forget_copy.txt b/src/docs/forget_copy.txt deleted file mode 100644 index 1d100912e9a4..000000000000 --- a/src/docs/forget_copy.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for calls to `std::mem::forget` with a value that -derives the Copy trait - -### Why is this bad? -Calling `std::mem::forget` [does nothing for types that -implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the -value will be copied and moved into the function on invocation. - -An alternative, but also valid, explanation is that Copy types do not -implement -the Drop trait, which means they have no destructors. Without a destructor, -there -is nothing for `std::mem::forget` to ignore. - -### Example -``` -let x: i32 = 42; // i32 implements Copy -std::mem::forget(x) // A copy of x is passed to the function, leaving the - // original unaffected -``` \ No newline at end of file diff --git a/src/docs/forget_non_drop.txt b/src/docs/forget_non_drop.txt deleted file mode 100644 index 3307d654c17f..000000000000 --- a/src/docs/forget_non_drop.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for calls to `std::mem::forget` with a value that does not implement `Drop`. - -### Why is this bad? -Calling `std::mem::forget` is no different than dropping such a type. A different value may -have been intended. - -### Example -``` -struct Foo; -let x = Foo; -std::mem::forget(x); -``` \ No newline at end of file diff --git a/src/docs/forget_ref.txt b/src/docs/forget_ref.txt deleted file mode 100644 index 874fb8786068..000000000000 --- a/src/docs/forget_ref.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for calls to `std::mem::forget` with a reference -instead of an owned value. - -### Why is this bad? -Calling `forget` on a reference will only forget the -reference itself, which is a no-op. It will not forget the underlying -referenced -value, which is likely what was intended. - -### Example -``` -let x = Box::new(1); -std::mem::forget(&x) // Should have been forget(x), x will still be dropped -``` \ No newline at end of file diff --git a/src/docs/format_in_format_args.txt b/src/docs/format_in_format_args.txt deleted file mode 100644 index ac498472f017..000000000000 --- a/src/docs/format_in_format_args.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Detects `format!` within the arguments of another macro that does -formatting such as `format!` itself, `write!` or `println!`. Suggests -inlining the `format!` call. - -### Why is this bad? -The recommended code is both shorter and avoids a temporary allocation. - -### Example -``` -println!("error: {}", format!("something failed at {}", Location::caller())); -``` -Use instead: -``` -println!("error: something failed at {}", Location::caller()); -``` \ No newline at end of file diff --git a/src/docs/format_push_string.txt b/src/docs/format_push_string.txt deleted file mode 100644 index ca409ebc7ec2..000000000000 --- a/src/docs/format_push_string.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Detects cases where the result of a `format!` call is -appended to an existing `String`. - -### Why is this bad? -Introduces an extra, avoidable heap allocation. - -### Known problems -`format!` returns a `String` but `write!` returns a `Result`. -Thus you are forced to ignore the `Err` variant to achieve the same API. - -While using `write!` in the suggested way should never fail, this isn't necessarily clear to the programmer. - -### Example -``` -let mut s = String::new(); -s += &format!("0x{:X}", 1024); -s.push_str(&format!("0x{:X}", 1024)); -``` -Use instead: -``` -use std::fmt::Write as _; // import without risk of name clashing - -let mut s = String::new(); -let _ = write!(s, "0x{:X}", 1024); -``` \ No newline at end of file diff --git a/src/docs/from_iter_instead_of_collect.txt b/src/docs/from_iter_instead_of_collect.txt deleted file mode 100644 index f3fd27597264..000000000000 --- a/src/docs/from_iter_instead_of_collect.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for `from_iter()` function calls on types that implement the `FromIterator` -trait. - -### Why is this bad? -It is recommended style to use collect. See -[FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) - -### Example -``` -let five_fives = std::iter::repeat(5).take(5); - -let v = Vec::from_iter(five_fives); - -assert_eq!(v, vec![5, 5, 5, 5, 5]); -``` -Use instead: -``` -let five_fives = std::iter::repeat(5).take(5); - -let v: Vec = five_fives.collect(); - -assert_eq!(v, vec![5, 5, 5, 5, 5]); -``` \ No newline at end of file diff --git a/src/docs/from_over_into.txt b/src/docs/from_over_into.txt deleted file mode 100644 index 0770bcc42c27..000000000000 --- a/src/docs/from_over_into.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead. - -### Why is this bad? -According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true. - -### Example -``` -struct StringWrapper(String); - -impl Into for String { - fn into(self) -> StringWrapper { - StringWrapper(self) - } -} -``` -Use instead: -``` -struct StringWrapper(String); - -impl From for StringWrapper { - fn from(s: String) -> StringWrapper { - StringWrapper(s) - } -} -``` \ No newline at end of file diff --git a/src/docs/from_str_radix_10.txt b/src/docs/from_str_radix_10.txt deleted file mode 100644 index f6f319d3eaa1..000000000000 --- a/src/docs/from_str_radix_10.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does - -Checks for function invocations of the form `primitive::from_str_radix(s, 10)` - -### Why is this bad? - -This specific common use case can be rewritten as `s.parse::()` -(and in most cases, the turbofish can be removed), which reduces code length -and complexity. - -### Known problems - -This lint may suggest using (&).parse() instead of .parse() directly -in some cases, which is correct but adds unnecessary complexity to the code. - -### Example -``` -let input: &str = get_input(); -let num = u16::from_str_radix(input, 10)?; -``` -Use instead: -``` -let input: &str = get_input(); -let num: u16 = input.parse()?; -``` \ No newline at end of file diff --git a/src/docs/future_not_send.txt b/src/docs/future_not_send.txt deleted file mode 100644 index 0aa048d27355..000000000000 --- a/src/docs/future_not_send.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -This lint requires Future implementations returned from -functions and methods to implement the `Send` marker trait. It is mostly -used by library authors (public and internal) that target an audience where -multithreaded executors are likely to be used for running these Futures. - -### Why is this bad? -A Future implementation captures some state that it -needs to eventually produce its final value. When targeting a multithreaded -executor (which is the norm on non-embedded devices) this means that this -state may need to be transported to other threads, in other words the -whole Future needs to implement the `Send` marker trait. If it does not, -then the resulting Future cannot be submitted to a thread pool in the -end user’s code. - -Especially for generic functions it can be confusing to leave the -discovery of this problem to the end user: the reported error location -will be far from its cause and can in many cases not even be fixed without -modifying the library where the offending Future implementation is -produced. - -### Example -``` -async fn not_send(bytes: std::rc::Rc<[u8]>) {} -``` -Use instead: -``` -async fn is_send(bytes: std::sync::Arc<[u8]>) {} -``` \ No newline at end of file diff --git a/src/docs/get_first.txt b/src/docs/get_first.txt deleted file mode 100644 index c905a737ddf3..000000000000 --- a/src/docs/get_first.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for using `x.get(0)` instead of -`x.first()`. - -### Why is this bad? -Using `x.first()` is easier to read and has the same -result. - -### Example -``` -let x = vec![2, 3, 5]; -let first_element = x.get(0); -``` - -Use instead: -``` -let x = vec![2, 3, 5]; -let first_element = x.first(); -``` \ No newline at end of file diff --git a/src/docs/get_last_with_len.txt b/src/docs/get_last_with_len.txt deleted file mode 100644 index 31c7f269586a..000000000000 --- a/src/docs/get_last_with_len.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for using `x.get(x.len() - 1)` instead of -`x.last()`. - -### Why is this bad? -Using `x.last()` is easier to read and has the same -result. - -Note that using `x[x.len() - 1]` is semantically different from -`x.last()`. Indexing into the array will panic on out-of-bounds -accesses, while `x.get()` and `x.last()` will return `None`. - -There is another lint (get_unwrap) that covers the case of using -`x.get(index).unwrap()` instead of `x[index]`. - -### Example -``` -let x = vec![2, 3, 5]; -let last_element = x.get(x.len() - 1); -``` - -Use instead: -``` -let x = vec![2, 3, 5]; -let last_element = x.last(); -``` \ No newline at end of file diff --git a/src/docs/get_unwrap.txt b/src/docs/get_unwrap.txt deleted file mode 100644 index 8defc2224416..000000000000 --- a/src/docs/get_unwrap.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for use of `.get().unwrap()` (or -`.get_mut().unwrap`) on a standard library type which implements `Index` - -### Why is this bad? -Using the Index trait (`[]`) is more clear and more -concise. - -### Known problems -Not a replacement for error handling: Using either -`.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic` -if the value being accessed is `None`. If the use of `.get().unwrap()` is a -temporary placeholder for dealing with the `Option` type, then this does -not mitigate the need for error handling. If there is a chance that `.get()` -will be `None` in your program, then it is advisable that the `None` case -is handled in a future refactor instead of using `.unwrap()` or the Index -trait. - -### Example -``` -let mut some_vec = vec![0, 1, 2, 3]; -let last = some_vec.get(3).unwrap(); -*some_vec.get_mut(0).unwrap() = 1; -``` -The correct use would be: -``` -let mut some_vec = vec![0, 1, 2, 3]; -let last = some_vec[3]; -some_vec[0] = 1; -``` \ No newline at end of file diff --git a/src/docs/identity_op.txt b/src/docs/identity_op.txt deleted file mode 100644 index a8e40bb43e9d..000000000000 --- a/src/docs/identity_op.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for identity operations, e.g., `x + 0`. - -### Why is this bad? -This code can be removed without changing the -meaning. So it just obscures what's going on. Delete it mercilessly. - -### Example -``` -x / 1 + 0 * 1 - 0 | 0; -``` \ No newline at end of file diff --git a/src/docs/if_let_mutex.txt b/src/docs/if_let_mutex.txt deleted file mode 100644 index 4d873ade9ace..000000000000 --- a/src/docs/if_let_mutex.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for `Mutex::lock` calls in `if let` expression -with lock calls in any of the else blocks. - -### Why is this bad? -The Mutex lock remains held for the whole -`if let ... else` block and deadlocks. - -### Example -``` -if let Ok(thing) = mutex.lock() { - do_thing(); -} else { - mutex.lock(); -} -``` -Should be written -``` -let locked = mutex.lock(); -if let Ok(thing) = locked { - do_thing(thing); -} else { - use_locked(locked); -} -``` \ No newline at end of file diff --git a/src/docs/if_not_else.txt b/src/docs/if_not_else.txt deleted file mode 100644 index 0e5ac4ce6bb8..000000000000 --- a/src/docs/if_not_else.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for usage of `!` or `!=` in an if condition with an -else branch. - -### Why is this bad? -Negations reduce the readability of statements. - -### Example -``` -if !v.is_empty() { - a() -} else { - b() -} -``` - -Could be written: - -``` -if v.is_empty() { - b() -} else { - a() -} -``` \ No newline at end of file diff --git a/src/docs/if_same_then_else.txt b/src/docs/if_same_then_else.txt deleted file mode 100644 index 75127016bb8c..000000000000 --- a/src/docs/if_same_then_else.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for `if/else` with the same body as the *then* part -and the *else* part. - -### Why is this bad? -This is probably a copy & paste error. - -### Example -``` -let foo = if … { - 42 -} else { - 42 -}; -``` \ No newline at end of file diff --git a/src/docs/if_then_some_else_none.txt b/src/docs/if_then_some_else_none.txt deleted file mode 100644 index 13744f920e36..000000000000 --- a/src/docs/if_then_some_else_none.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for if-else that could be written using either `bool::then` or `bool::then_some`. - -### Why is this bad? -Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity. -For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated -in comparison to `bool::then`. - -### Example -``` -let a = if v.is_empty() { - println!("true!"); - Some(42) -} else { - None -}; -``` - -Could be written: - -``` -let a = v.is_empty().then(|| { - println!("true!"); - 42 -}); -``` \ No newline at end of file diff --git a/src/docs/ifs_same_cond.txt b/src/docs/ifs_same_cond.txt deleted file mode 100644 index 024ba5df93a6..000000000000 --- a/src/docs/ifs_same_cond.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for consecutive `if`s with the same condition. - -### Why is this bad? -This is probably a copy & paste error. - -### Example -``` -if a == b { - … -} else if a == b { - … -} -``` - -Note that this lint ignores all conditions with a function call as it could -have side effects: - -``` -if foo() { - … -} else if foo() { // not linted - … -} -``` \ No newline at end of file diff --git a/src/docs/implicit_clone.txt b/src/docs/implicit_clone.txt deleted file mode 100644 index f5aa112c52c3..000000000000 --- a/src/docs/implicit_clone.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer. - -### Why is this bad? -These methods do the same thing as `_.clone()` but may be confusing as -to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned. - -### Example -``` -let a = vec![1, 2, 3]; -let b = a.to_vec(); -let c = a.to_owned(); -``` -Use instead: -``` -let a = vec![1, 2, 3]; -let b = a.clone(); -let c = a.clone(); -``` \ No newline at end of file diff --git a/src/docs/implicit_hasher.txt b/src/docs/implicit_hasher.txt deleted file mode 100644 index 0c1f76620f51..000000000000 --- a/src/docs/implicit_hasher.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for public `impl` or `fn` missing generalization -over different hashers and implicitly defaulting to the default hashing -algorithm (`SipHash`). - -### Why is this bad? -`HashMap` or `HashSet` with custom hashers cannot be -used with them. - -### Known problems -Suggestions for replacing constructors can contain -false-positives. Also applying suggestions can require modification of other -pieces of code, possibly including external crates. - -### Example -``` -impl Serialize for HashMap { } - -pub fn foo(map: &mut HashMap) { } -``` -could be rewritten as -``` -impl Serialize for HashMap { } - -pub fn foo(map: &mut HashMap) { } -``` \ No newline at end of file diff --git a/src/docs/implicit_return.txt b/src/docs/implicit_return.txt deleted file mode 100644 index ee65a636b38c..000000000000 --- a/src/docs/implicit_return.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for missing return statements at the end of a block. - -### Why is this bad? -Actually omitting the return keyword is idiomatic Rust code. Programmers -coming from other languages might prefer the expressiveness of `return`. It's possible to miss -the last returning statement because the only difference is a missing `;`. Especially in bigger -code with multiple return paths having a `return` keyword makes it easier to find the -corresponding statements. - -### Example -``` -fn foo(x: usize) -> usize { - x -} -``` -add return -``` -fn foo(x: usize) -> usize { - return x; -} -``` \ No newline at end of file diff --git a/src/docs/implicit_saturating_add.txt b/src/docs/implicit_saturating_add.txt deleted file mode 100644 index 5883a5363e2b..000000000000 --- a/src/docs/implicit_saturating_add.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for implicit saturating addition. - -### Why is this bad? -The built-in function is more readable and may be faster. - -### Example -``` -let mut u:u32 = 7000; - -if u != u32::MAX { - u += 1; -} -``` -Use instead: -``` -let mut u:u32 = 7000; - -u = u.saturating_add(1); -``` \ No newline at end of file diff --git a/src/docs/implicit_saturating_sub.txt b/src/docs/implicit_saturating_sub.txt deleted file mode 100644 index 03b47905a211..000000000000 --- a/src/docs/implicit_saturating_sub.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for implicit saturating subtraction. - -### Why is this bad? -Simplicity and readability. Instead we can easily use an builtin function. - -### Example -``` -let mut i: u32 = end - start; - -if i != 0 { - i -= 1; -} -``` - -Use instead: -``` -let mut i: u32 = end - start; - -i = i.saturating_sub(1); -``` \ No newline at end of file diff --git a/src/docs/imprecise_flops.txt b/src/docs/imprecise_flops.txt deleted file mode 100644 index e84d81cea98e..000000000000 --- a/src/docs/imprecise_flops.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Looks for floating-point expressions that -can be expressed using built-in methods to improve accuracy -at the cost of performance. - -### Why is this bad? -Negatively impacts accuracy. - -### Example -``` -let a = 3f32; -let _ = a.powf(1.0 / 3.0); -let _ = (1.0 + a).ln(); -let _ = a.exp() - 1.0; -``` - -Use instead: -``` -let a = 3f32; -let _ = a.cbrt(); -let _ = a.ln_1p(); -let _ = a.exp_m1(); -``` \ No newline at end of file diff --git a/src/docs/inconsistent_digit_grouping.txt b/src/docs/inconsistent_digit_grouping.txt deleted file mode 100644 index aa0b072de1c4..000000000000 --- a/src/docs/inconsistent_digit_grouping.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Warns if an integral or floating-point constant is -grouped inconsistently with underscores. - -### Why is this bad? -Readers may incorrectly interpret inconsistently -grouped digits. - -### Example -``` -618_64_9189_73_511 -``` - -Use instead: -``` -61_864_918_973_511 -``` \ No newline at end of file diff --git a/src/docs/inconsistent_struct_constructor.txt b/src/docs/inconsistent_struct_constructor.txt deleted file mode 100644 index eb682109a54e..000000000000 --- a/src/docs/inconsistent_struct_constructor.txt +++ /dev/null @@ -1,40 +0,0 @@ -### What it does -Checks for struct constructors where all fields are shorthand and -the order of the field init shorthand in the constructor is inconsistent -with the order in the struct definition. - -### Why is this bad? -Since the order of fields in a constructor doesn't affect the -resulted instance as the below example indicates, - -``` -#[derive(Debug, PartialEq, Eq)] -struct Foo { - x: i32, - y: i32, -} -let x = 1; -let y = 2; - -// This assertion never fails: -assert_eq!(Foo { x, y }, Foo { y, x }); -``` - -inconsistent order can be confusing and decreases readability and consistency. - -### Example -``` -struct Foo { - x: i32, - y: i32, -} -let x = 1; -let y = 2; - -Foo { y, x }; -``` - -Use instead: -``` -Foo { x, y }; -``` \ No newline at end of file diff --git a/src/docs/index_refutable_slice.txt b/src/docs/index_refutable_slice.txt deleted file mode 100644 index 8a7d52761af8..000000000000 --- a/src/docs/index_refutable_slice.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -The lint checks for slice bindings in patterns that are only used to -access individual slice values. - -### Why is this bad? -Accessing slice values using indices can lead to panics. Using refutable -patterns can avoid these. Binding to individual values also improves the -readability as they can be named. - -### Limitations -This lint currently only checks for immutable access inside `if let` -patterns. - -### Example -``` -let slice: Option<&[u32]> = Some(&[1, 2, 3]); - -if let Some(slice) = slice { - println!("{}", slice[0]); -} -``` -Use instead: -``` -let slice: Option<&[u32]> = Some(&[1, 2, 3]); - -if let Some(&[first, ..]) = slice { - println!("{}", first); -} -``` \ No newline at end of file diff --git a/src/docs/indexing_slicing.txt b/src/docs/indexing_slicing.txt deleted file mode 100644 index 76ca6ed318b3..000000000000 --- a/src/docs/indexing_slicing.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks for usage of indexing or slicing. Arrays are special cases, this lint -does report on arrays if we can tell that slicing operations are in bounds and does not -lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint. - -### Why is this bad? -Indexing and slicing can panic at runtime and there are -safe alternatives. - -### Example -``` -// Vector -let x = vec![0; 5]; - -x[2]; -&x[2..100]; - -// Array -let y = [0, 1, 2, 3]; - -&y[10..100]; -&y[10..]; -``` - -Use instead: -``` - -x.get(2); -x.get(2..100); - -y.get(10); -y.get(10..100); -``` \ No newline at end of file diff --git a/src/docs/ineffective_bit_mask.txt b/src/docs/ineffective_bit_mask.txt deleted file mode 100644 index f6e7ef556215..000000000000 --- a/src/docs/ineffective_bit_mask.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for bit masks in comparisons which can be removed -without changing the outcome. The basic structure can be seen in the -following table: - -|Comparison| Bit Op |Example |equals | -|----------|----------|------------|-------| -|`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`| -|`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`| - -### Why is this bad? -Not equally evil as [`bad_bit_mask`](#bad_bit_mask), -but still a bit misleading, because the bit mask is ineffective. - -### Known problems -False negatives: This lint will only match instances -where we have figured out the math (which is for a power-of-two compared -value). This means things like `x | 1 >= 7` (which would be better written -as `x >= 6`) will not be reported (but bit masks like this are fairly -uncommon). - -### Example -``` -if (x | 1 > 3) { } -``` \ No newline at end of file diff --git a/src/docs/inefficient_to_string.txt b/src/docs/inefficient_to_string.txt deleted file mode 100644 index f7061d1ce7b0..000000000000 --- a/src/docs/inefficient_to_string.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for usage of `.to_string()` on an `&&T` where -`T` implements `ToString` directly (like `&&str` or `&&String`). - -### Why is this bad? -This bypasses the specialized implementation of -`ToString` and instead goes through the more expensive string formatting -facilities. - -### Example -``` -// Generic implementation for `T: Display` is used (slow) -["foo", "bar"].iter().map(|s| s.to_string()); - -// OK, the specialized impl is used -["foo", "bar"].iter().map(|&s| s.to_string()); -``` \ No newline at end of file diff --git a/src/docs/infallible_destructuring_match.txt b/src/docs/infallible_destructuring_match.txt deleted file mode 100644 index 4b5d3c4ba6c4..000000000000 --- a/src/docs/infallible_destructuring_match.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for matches being used to destructure a single-variant enum -or tuple struct where a `let` will suffice. - -### Why is this bad? -Just readability – `let` doesn't nest, whereas a `match` does. - -### Example -``` -enum Wrapper { - Data(i32), -} - -let wrapper = Wrapper::Data(42); - -let data = match wrapper { - Wrapper::Data(i) => i, -}; -``` - -The correct use would be: -``` -enum Wrapper { - Data(i32), -} - -let wrapper = Wrapper::Data(42); -let Wrapper::Data(data) = wrapper; -``` \ No newline at end of file diff --git a/src/docs/infinite_iter.txt b/src/docs/infinite_iter.txt deleted file mode 100644 index 8a22fabc5492..000000000000 --- a/src/docs/infinite_iter.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for iteration that is guaranteed to be infinite. - -### Why is this bad? -While there may be places where this is acceptable -(e.g., in event streams), in most cases this is simply an error. - -### Example -``` -use std::iter; - -iter::repeat(1_u8).collect::>(); -``` \ No newline at end of file diff --git a/src/docs/inherent_to_string.txt b/src/docs/inherent_to_string.txt deleted file mode 100644 index b18e600e9e67..000000000000 --- a/src/docs/inherent_to_string.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`. - -### Why is this bad? -This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred. - -### Example -``` -pub struct A; - -impl A { - pub fn to_string(&self) -> String { - "I am A".to_string() - } -} -``` - -Use instead: -``` -use std::fmt; - -pub struct A; - -impl fmt::Display for A { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "I am A") - } -} -``` \ No newline at end of file diff --git a/src/docs/inherent_to_string_shadow_display.txt b/src/docs/inherent_to_string_shadow_display.txt deleted file mode 100644 index a4bd0b622c4f..000000000000 --- a/src/docs/inherent_to_string_shadow_display.txt +++ /dev/null @@ -1,37 +0,0 @@ -### What it does -Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait. - -### Why is this bad? -This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`. - -### Example -``` -use std::fmt; - -pub struct A; - -impl A { - pub fn to_string(&self) -> String { - "I am A".to_string() - } -} - -impl fmt::Display for A { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "I am A, too") - } -} -``` - -Use instead: -``` -use std::fmt; - -pub struct A; - -impl fmt::Display for A { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "I am A") - } -} -``` \ No newline at end of file diff --git a/src/docs/init_numbered_fields.txt b/src/docs/init_numbered_fields.txt deleted file mode 100644 index ba40af6a5fa5..000000000000 --- a/src/docs/init_numbered_fields.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for tuple structs initialized with field syntax. -It will however not lint if a base initializer is present. -The lint will also ignore code in macros. - -### Why is this bad? -This may be confusing to the uninitiated and adds no -benefit as opposed to tuple initializers - -### Example -``` -struct TupleStruct(u8, u16); - -let _ = TupleStruct { - 0: 1, - 1: 23, -}; - -// should be written as -let base = TupleStruct(1, 23); - -// This is OK however -let _ = TupleStruct { 0: 42, ..base }; -``` \ No newline at end of file diff --git a/src/docs/inline_always.txt b/src/docs/inline_always.txt deleted file mode 100644 index 7721da4c4cc7..000000000000 --- a/src/docs/inline_always.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for items annotated with `#[inline(always)]`, -unless the annotated function is empty or simply panics. - -### Why is this bad? -While there are valid uses of this annotation (and once -you know when to use it, by all means `allow` this lint), it's a common -newbie-mistake to pepper one's code with it. - -As a rule of thumb, before slapping `#[inline(always)]` on a function, -measure if that additional function call really affects your runtime profile -sufficiently to make up for the increase in compile time. - -### Known problems -False positives, big time. This lint is meant to be -deactivated by everyone doing serious performance work. This means having -done the measurement. - -### Example -``` -#[inline(always)] -fn not_quite_hot_code(..) { ... } -``` \ No newline at end of file diff --git a/src/docs/inline_asm_x86_att_syntax.txt b/src/docs/inline_asm_x86_att_syntax.txt deleted file mode 100644 index 8eb49d122d89..000000000000 --- a/src/docs/inline_asm_x86_att_syntax.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of AT&T x86 assembly syntax. - -### Why is this bad? -The lint has been enabled to indicate a preference -for Intel x86 assembly syntax. - -### Example - -``` -asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); -``` -Use instead: -``` -asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); -``` \ No newline at end of file diff --git a/src/docs/inline_asm_x86_intel_syntax.txt b/src/docs/inline_asm_x86_intel_syntax.txt deleted file mode 100644 index 5aa22c8ed235..000000000000 --- a/src/docs/inline_asm_x86_intel_syntax.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of Intel x86 assembly syntax. - -### Why is this bad? -The lint has been enabled to indicate a preference -for AT&T x86 assembly syntax. - -### Example - -``` -asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); -``` -Use instead: -``` -asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); -``` \ No newline at end of file diff --git a/src/docs/inline_fn_without_body.txt b/src/docs/inline_fn_without_body.txt deleted file mode 100644 index 127c161aaa25..000000000000 --- a/src/docs/inline_fn_without_body.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for `#[inline]` on trait methods without bodies - -### Why is this bad? -Only implementations of trait methods may be inlined. -The inline attribute is ignored for trait methods without bodies. - -### Example -``` -trait Animal { - #[inline] - fn name(&self) -> &'static str; -} -``` \ No newline at end of file diff --git a/src/docs/inspect_for_each.txt b/src/docs/inspect_for_each.txt deleted file mode 100644 index 01a46d6c451f..000000000000 --- a/src/docs/inspect_for_each.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for usage of `inspect().for_each()`. - -### Why is this bad? -It is the same as performing the computation -inside `inspect` at the beginning of the closure in `for_each`. - -### Example -``` -[1,2,3,4,5].iter() -.inspect(|&x| println!("inspect the number: {}", x)) -.for_each(|&x| { - assert!(x >= 0); -}); -``` -Can be written as -``` -[1,2,3,4,5].iter() -.for_each(|&x| { - println!("inspect the number: {}", x); - assert!(x >= 0); -}); -``` \ No newline at end of file diff --git a/src/docs/int_plus_one.txt b/src/docs/int_plus_one.txt deleted file mode 100644 index 1b68f3eeb64b..000000000000 --- a/src/docs/int_plus_one.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block - -### Why is this bad? -Readability -- better to use `> y` instead of `>= y + 1`. - -### Example -``` -if x >= y + 1 {} -``` - -Use instead: -``` -if x > y {} -``` \ No newline at end of file diff --git a/src/docs/integer_arithmetic.txt b/src/docs/integer_arithmetic.txt deleted file mode 100644 index ea57a2ef97bf..000000000000 --- a/src/docs/integer_arithmetic.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for integer arithmetic operations which could overflow or panic. - -Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable -of overflowing according to the [Rust -Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), -or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is -attempted. - -### Why is this bad? -Integer overflow will trigger a panic in debug builds or will wrap in -release mode. Division by zero will cause a panic in either mode. In some applications one -wants explicitly checked, wrapping or saturating arithmetic. - -### Example -``` -a + 1; -``` \ No newline at end of file diff --git a/src/docs/integer_division.txt b/src/docs/integer_division.txt deleted file mode 100644 index f6d3349810ed..000000000000 --- a/src/docs/integer_division.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for division of integers - -### Why is this bad? -When outside of some very specific algorithms, -integer division is very often a mistake because it discards the -remainder. - -### Example -``` -let x = 3 / 2; -println!("{}", x); -``` - -Use instead: -``` -let x = 3f32 / 2f32; -println!("{}", x); -``` \ No newline at end of file diff --git a/src/docs/into_iter_on_ref.txt b/src/docs/into_iter_on_ref.txt deleted file mode 100644 index acb6bd474ebf..000000000000 --- a/src/docs/into_iter_on_ref.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for `into_iter` calls on references which should be replaced by `iter` -or `iter_mut`. - -### Why is this bad? -Readability. Calling `into_iter` on a reference will not move out its -content into the resulting iterator, which is confusing. It is better just call `iter` or -`iter_mut` directly. - -### Example -``` -(&vec).into_iter(); -``` - -Use instead: -``` -(&vec).iter(); -``` \ No newline at end of file diff --git a/src/docs/invalid_null_ptr_usage.txt b/src/docs/invalid_null_ptr_usage.txt deleted file mode 100644 index 6fb3fa3f83d6..000000000000 --- a/src/docs/invalid_null_ptr_usage.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint checks for invalid usages of `ptr::null`. - -### Why is this bad? -This causes undefined behavior. - -### Example -``` -// Undefined behavior -unsafe { std::slice::from_raw_parts(ptr::null(), 0); } -``` - -Use instead: -``` -unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } -``` \ No newline at end of file diff --git a/src/docs/invalid_regex.txt b/src/docs/invalid_regex.txt deleted file mode 100644 index 6c9969b6e1a3..000000000000 --- a/src/docs/invalid_regex.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks [regex](https://crates.io/crates/regex) creation -(with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct -regex syntax. - -### Why is this bad? -This will lead to a runtime panic. - -### Example -``` -Regex::new("(") -``` \ No newline at end of file diff --git a/src/docs/invalid_upcast_comparisons.txt b/src/docs/invalid_upcast_comparisons.txt deleted file mode 100644 index 77cb03308037..000000000000 --- a/src/docs/invalid_upcast_comparisons.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for comparisons where the relation is always either -true or false, but where one side has been upcast so that the comparison is -necessary. Only integer types are checked. - -### Why is this bad? -An expression like `let x : u8 = ...; (x as u32) > 300` -will mistakenly imply that it is possible for `x` to be outside the range of -`u8`. - -### Known problems -https://github.com/rust-lang/rust-clippy/issues/886 - -### Example -``` -let x: u8 = 1; -(x as u32) > 300; -``` \ No newline at end of file diff --git a/src/docs/invalid_utf8_in_unchecked.txt b/src/docs/invalid_utf8_in_unchecked.txt deleted file mode 100644 index afb5acbe9c51..000000000000 --- a/src/docs/invalid_utf8_in_unchecked.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for `std::str::from_utf8_unchecked` with an invalid UTF-8 literal - -### Why is this bad? -Creating such a `str` would result in undefined behavior - -### Example -``` -unsafe { - std::str::from_utf8_unchecked(b"cl\x82ippy"); -} -``` \ No newline at end of file diff --git a/src/docs/invisible_characters.txt b/src/docs/invisible_characters.txt deleted file mode 100644 index 3dda380911f9..000000000000 --- a/src/docs/invisible_characters.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for invisible Unicode characters in the code. - -### Why is this bad? -Having an invisible character in the code makes for all -sorts of April fools, but otherwise is very much frowned upon. - -### Example -You don't see it, but there may be a zero-width space or soft hyphen -some­where in this text. \ No newline at end of file diff --git a/src/docs/is_digit_ascii_radix.txt b/src/docs/is_digit_ascii_radix.txt deleted file mode 100644 index 9f11cf43054f..000000000000 --- a/src/docs/is_digit_ascii_radix.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that -can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or -[`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit). - -### Why is this bad? -`is_digit(..)` is slower and requires specifying the radix. - -### Example -``` -let c: char = '6'; -c.is_digit(10); -c.is_digit(16); -``` -Use instead: -``` -let c: char = '6'; -c.is_ascii_digit(); -c.is_ascii_hexdigit(); -``` \ No newline at end of file diff --git a/src/docs/items_after_statements.txt b/src/docs/items_after_statements.txt deleted file mode 100644 index 6fdfff50d20e..000000000000 --- a/src/docs/items_after_statements.txt +++ /dev/null @@ -1,37 +0,0 @@ -### What it does -Checks for items declared after some statement in a block. - -### Why is this bad? -Items live for the entire scope they are declared -in. But statements are processed in order. This might cause confusion as -it's hard to figure out which item is meant in a statement. - -### Example -``` -fn foo() { - println!("cake"); -} - -fn main() { - foo(); // prints "foo" - fn foo() { - println!("foo"); - } - foo(); // prints "foo" -} -``` - -Use instead: -``` -fn foo() { - println!("cake"); -} - -fn main() { - fn foo() { - println!("foo"); - } - foo(); // prints "foo" - foo(); // prints "foo" -} -``` \ No newline at end of file diff --git a/src/docs/iter_cloned_collect.txt b/src/docs/iter_cloned_collect.txt deleted file mode 100644 index 90dc9ebb40f0..000000000000 --- a/src/docs/iter_cloned_collect.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for the use of `.cloned().collect()` on slice to -create a `Vec`. - -### Why is this bad? -`.to_vec()` is clearer - -### Example -``` -let s = [1, 2, 3, 4, 5]; -let s2: Vec = s[..].iter().cloned().collect(); -``` -The better use would be: -``` -let s = [1, 2, 3, 4, 5]; -let s2: Vec = s.to_vec(); -``` \ No newline at end of file diff --git a/src/docs/iter_count.txt b/src/docs/iter_count.txt deleted file mode 100644 index f3db4a26c299..000000000000 --- a/src/docs/iter_count.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for the use of `.iter().count()`. - -### Why is this bad? -`.len()` is more efficient and more -readable. - -### Example -``` -let some_vec = vec![0, 1, 2, 3]; - -some_vec.iter().count(); -&some_vec[..].iter().count(); -``` - -Use instead: -``` -let some_vec = vec![0, 1, 2, 3]; - -some_vec.len(); -&some_vec[..].len(); -``` \ No newline at end of file diff --git a/src/docs/iter_kv_map.txt b/src/docs/iter_kv_map.txt deleted file mode 100644 index a063c8195ef5..000000000000 --- a/src/docs/iter_kv_map.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does - -Checks for iterating a map (`HashMap` or `BTreeMap`) and -ignoring either the keys or values. - -### Why is this bad? - -Readability. There are `keys` and `values` methods that -can be used to express that we only need the keys or the values. - -### Example - -``` -let map: HashMap = HashMap::new(); -let values = map.iter().map(|(_, value)| value).collect::>(); -``` - -Use instead: -``` -let map: HashMap = HashMap::new(); -let values = map.values().collect::>(); -``` \ No newline at end of file diff --git a/src/docs/iter_next_loop.txt b/src/docs/iter_next_loop.txt deleted file mode 100644 index b33eb39d6e1d..000000000000 --- a/src/docs/iter_next_loop.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for loops on `x.next()`. - -### Why is this bad? -`next()` returns either `Some(value)` if there was a -value, or `None` otherwise. The insidious thing is that `Option<_>` -implements `IntoIterator`, so that possibly one value will be iterated, -leading to some hard to find bugs. No one will want to write such code -[except to win an Underhanded Rust -Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr). - -### Example -``` -for x in y.next() { - .. -} -``` \ No newline at end of file diff --git a/src/docs/iter_next_slice.txt b/src/docs/iter_next_slice.txt deleted file mode 100644 index 1cea25eaf301..000000000000 --- a/src/docs/iter_next_slice.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `iter().next()` on a Slice or an Array - -### Why is this bad? -These can be shortened into `.get()` - -### Example -``` -a[2..].iter().next(); -b.iter().next(); -``` -should be written as: -``` -a.get(2); -b.get(0); -``` \ No newline at end of file diff --git a/src/docs/iter_not_returning_iterator.txt b/src/docs/iter_not_returning_iterator.txt deleted file mode 100644 index 0ca862910a6f..000000000000 --- a/src/docs/iter_not_returning_iterator.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Detects methods named `iter` or `iter_mut` that do not have a return type that implements `Iterator`. - -### Why is this bad? -Methods named `iter` or `iter_mut` conventionally return an `Iterator`. - -### Example -``` -// `String` does not implement `Iterator` -struct Data {} -impl Data { - fn iter(&self) -> String { - todo!() - } -} -``` -Use instead: -``` -use std::str::Chars; -struct Data {} -impl Data { - fn iter(&self) -> Chars<'static> { - todo!() - } -} -``` \ No newline at end of file diff --git a/src/docs/iter_nth.txt b/src/docs/iter_nth.txt deleted file mode 100644 index 3d67d583ffde..000000000000 --- a/src/docs/iter_nth.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for use of `.iter().nth()` (and the related -`.iter_mut().nth()`) on standard library types with *O*(1) element access. - -### Why is this bad? -`.get()` and `.get_mut()` are more efficient and more -readable. - -### Example -``` -let some_vec = vec![0, 1, 2, 3]; -let bad_vec = some_vec.iter().nth(3); -let bad_slice = &some_vec[..].iter().nth(3); -``` -The correct use would be: -``` -let some_vec = vec![0, 1, 2, 3]; -let bad_vec = some_vec.get(3); -let bad_slice = &some_vec[..].get(3); -``` \ No newline at end of file diff --git a/src/docs/iter_nth_zero.txt b/src/docs/iter_nth_zero.txt deleted file mode 100644 index 8efe47a16a10..000000000000 --- a/src/docs/iter_nth_zero.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for the use of `iter.nth(0)`. - -### Why is this bad? -`iter.next()` is equivalent to -`iter.nth(0)`, as they both consume the next element, - but is more readable. - -### Example -``` -let x = s.iter().nth(0); -``` - -Use instead: -``` -let x = s.iter().next(); -``` \ No newline at end of file diff --git a/src/docs/iter_on_empty_collections.txt b/src/docs/iter_on_empty_collections.txt deleted file mode 100644 index 87c4ec12afae..000000000000 --- a/src/docs/iter_on_empty_collections.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does - -Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections - -### Why is this bad? - -It is simpler to use the empty function from the standard library: - -### Example - -``` -use std::{slice, option}; -let a: slice::Iter = [].iter(); -let f: option::IntoIter = None.into_iter(); -``` -Use instead: -``` -use std::iter; -let a: iter::Empty = iter::empty(); -let b: iter::Empty = iter::empty(); -``` - -### Known problems - -The type of the resulting iterator might become incompatible with its usage \ No newline at end of file diff --git a/src/docs/iter_on_single_items.txt b/src/docs/iter_on_single_items.txt deleted file mode 100644 index d0388f25d045..000000000000 --- a/src/docs/iter_on_single_items.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does - -Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item - -### Why is this bad? - -It is simpler to use the once function from the standard library: - -### Example - -``` -let a = [123].iter(); -let b = Some(123).into_iter(); -``` -Use instead: -``` -use std::iter; -let a = iter::once(&123); -let b = iter::once(123); -``` - -### Known problems - -The type of the resulting iterator might become incompatible with its usage \ No newline at end of file diff --git a/src/docs/iter_overeager_cloned.txt b/src/docs/iter_overeager_cloned.txt deleted file mode 100644 index 2f902a0c2db4..000000000000 --- a/src/docs/iter_overeager_cloned.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `_.cloned().()` where call to `.cloned()` can be postponed. - -### Why is this bad? -It's often inefficient to clone all elements of an iterator, when eventually, only some -of them will be consumed. - -### Known Problems -This `lint` removes the side of effect of cloning items in the iterator. -A code that relies on that side-effect could fail. - -### Examples -``` -vec.iter().cloned().take(10); -vec.iter().cloned().last(); -``` - -Use instead: -``` -vec.iter().take(10).cloned(); -vec.iter().last().cloned(); -``` \ No newline at end of file diff --git a/src/docs/iter_skip_next.txt b/src/docs/iter_skip_next.txt deleted file mode 100644 index da226b041cf2..000000000000 --- a/src/docs/iter_skip_next.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for use of `.skip(x).next()` on iterators. - -### Why is this bad? -`.nth(x)` is cleaner - -### Example -``` -let some_vec = vec![0, 1, 2, 3]; -let bad_vec = some_vec.iter().skip(3).next(); -let bad_slice = &some_vec[..].iter().skip(3).next(); -``` -The correct use would be: -``` -let some_vec = vec![0, 1, 2, 3]; -let bad_vec = some_vec.iter().nth(3); -let bad_slice = &some_vec[..].iter().nth(3); -``` \ No newline at end of file diff --git a/src/docs/iter_with_drain.txt b/src/docs/iter_with_drain.txt deleted file mode 100644 index 2c52b99f7a5c..000000000000 --- a/src/docs/iter_with_drain.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration. - -### Why is this bad? -`.into_iter()` is simpler with better performance. - -### Example -``` -let mut foo = vec![0, 1, 2, 3]; -let bar: HashSet = foo.drain(..).collect(); -``` -Use instead: -``` -let foo = vec![0, 1, 2, 3]; -let bar: HashSet = foo.into_iter().collect(); -``` \ No newline at end of file diff --git a/src/docs/iterator_step_by_zero.txt b/src/docs/iterator_step_by_zero.txt deleted file mode 100644 index 73ecc99acfcb..000000000000 --- a/src/docs/iterator_step_by_zero.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for calling `.step_by(0)` on iterators which panics. - -### Why is this bad? -This very much looks like an oversight. Use `panic!()` instead if you -actually intend to panic. - -### Example -``` -for x in (0..100).step_by(0) { - //.. -} -``` \ No newline at end of file diff --git a/src/docs/just_underscores_and_digits.txt b/src/docs/just_underscores_and_digits.txt deleted file mode 100644 index a8790bcf25be..000000000000 --- a/src/docs/just_underscores_and_digits.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks if you have variables whose name consists of just -underscores and digits. - -### Why is this bad? -It's hard to memorize what a variable means without a -descriptive name. - -### Example -``` -let _1 = 1; -let ___1 = 1; -let __1___2 = 11; -``` \ No newline at end of file diff --git a/src/docs/large_const_arrays.txt b/src/docs/large_const_arrays.txt deleted file mode 100644 index 71f67854f2a1..000000000000 --- a/src/docs/large_const_arrays.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for large `const` arrays that should -be defined as `static` instead. - -### Why is this bad? -Performance: const variables are inlined upon use. -Static items result in only one instance and has a fixed location in memory. - -### Example -``` -pub const a = [0u32; 1_000_000]; -``` - -Use instead: -``` -pub static a = [0u32; 1_000_000]; -``` \ No newline at end of file diff --git a/src/docs/large_digit_groups.txt b/src/docs/large_digit_groups.txt deleted file mode 100644 index f60b19345af4..000000000000 --- a/src/docs/large_digit_groups.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Warns if the digits of an integral or floating-point -constant are grouped into groups that -are too large. - -### Why is this bad? -Negatively impacts readability. - -### Example -``` -let x: u64 = 6186491_8973511; -``` \ No newline at end of file diff --git a/src/docs/large_enum_variant.txt b/src/docs/large_enum_variant.txt deleted file mode 100644 index 1f95430790d2..000000000000 --- a/src/docs/large_enum_variant.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Checks for large size differences between variants on -`enum`s. - -### Why is this bad? -Enum size is bounded by the largest variant. Having one -large variant can penalize the memory layout of that enum. - -### Known problems -This lint obviously cannot take the distribution of -variants in your running program into account. It is possible that the -smaller variants make up less than 1% of all instances, in which case -the overhead is negligible and the boxing is counter-productive. Always -measure the change this lint suggests. - -For types that implement `Copy`, the suggestion to `Box` a variant's -data would require removing the trait impl. The types can of course -still be `Clone`, but that is worse ergonomically. Depending on the -use case it may be possible to store the large data in an auxiliary -structure (e.g. Arena or ECS). - -The lint will ignore the impact of generic types to the type layout by -assuming every type parameter is zero-sized. Depending on your use case, -this may lead to a false positive. - -### Example -``` -enum Test { - A(i32), - B([i32; 8000]), -} -``` - -Use instead: -``` -// Possibly better -enum Test2 { - A(i32), - B(Box<[i32; 8000]>), -} -``` \ No newline at end of file diff --git a/src/docs/large_include_file.txt b/src/docs/large_include_file.txt deleted file mode 100644 index b2a54bd2eb5c..000000000000 --- a/src/docs/large_include_file.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for the inclusion of large files via `include_bytes!()` -and `include_str!()` - -### Why is this bad? -Including large files can increase the size of the binary - -### Example -``` -let included_str = include_str!("very_large_file.txt"); -let included_bytes = include_bytes!("very_large_file.txt"); -``` - -Use instead: -``` -use std::fs; - -// You can load the file at runtime -let string = fs::read_to_string("very_large_file.txt")?; -let bytes = fs::read("very_large_file.txt")?; -``` \ No newline at end of file diff --git a/src/docs/large_stack_arrays.txt b/src/docs/large_stack_arrays.txt deleted file mode 100644 index 4a6f34785b0e..000000000000 --- a/src/docs/large_stack_arrays.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for local arrays that may be too large. - -### Why is this bad? -Large local arrays may cause stack overflow. - -### Example -``` -let a = [0u32; 1_000_000]; -``` \ No newline at end of file diff --git a/src/docs/large_types_passed_by_value.txt b/src/docs/large_types_passed_by_value.txt deleted file mode 100644 index bca07f3ac61b..000000000000 --- a/src/docs/large_types_passed_by_value.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for functions taking arguments by value, where -the argument type is `Copy` and large enough to be worth considering -passing by reference. Does not trigger if the function is being exported, -because that might induce API breakage, if the parameter is declared as mutable, -or if the argument is a `self`. - -### Why is this bad? -Arguments passed by value might result in an unnecessary -shallow copy, taking up more space in the stack and requiring a call to -`memcpy`, which can be expensive. - -### Example -``` -#[derive(Clone, Copy)] -struct TooLarge([u8; 2048]); - -fn foo(v: TooLarge) {} -``` - -Use instead: -``` -fn foo(v: &TooLarge) {} -``` \ No newline at end of file diff --git a/src/docs/len_without_is_empty.txt b/src/docs/len_without_is_empty.txt deleted file mode 100644 index 47a2e8575228..000000000000 --- a/src/docs/len_without_is_empty.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for items that implement `.len()` but not -`.is_empty()`. - -### Why is this bad? -It is good custom to have both methods, because for -some data structures, asking about the length will be a costly operation, -whereas `.is_empty()` can usually answer in constant time. Also it used to -lead to false positives on the [`len_zero`](#len_zero) lint – currently that -lint will ignore such entities. - -### Example -``` -impl X { - pub fn len(&self) -> usize { - .. - } -} -``` \ No newline at end of file diff --git a/src/docs/len_zero.txt b/src/docs/len_zero.txt deleted file mode 100644 index 664124bd391d..000000000000 --- a/src/docs/len_zero.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for getting the length of something via `.len()` -just to compare to zero, and suggests using `.is_empty()` where applicable. - -### Why is this bad? -Some structures can answer `.is_empty()` much faster -than calculating their length. So it is good to get into the habit of using -`.is_empty()`, and having it is cheap. -Besides, it makes the intent clearer than a manual comparison in some contexts. - -### Example -``` -if x.len() == 0 { - .. -} -if y.len() != 0 { - .. -} -``` -instead use -``` -if x.is_empty() { - .. -} -if !y.is_empty() { - .. -} -``` \ No newline at end of file diff --git a/src/docs/let_and_return.txt b/src/docs/let_and_return.txt deleted file mode 100644 index eba5a90ddd66..000000000000 --- a/src/docs/let_and_return.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for `let`-bindings, which are subsequently -returned. - -### Why is this bad? -It is just extraneous code. Remove it to make your code -more rusty. - -### Example -``` -fn foo() -> String { - let x = String::new(); - x -} -``` -instead, use -``` -fn foo() -> String { - String::new() -} -``` \ No newline at end of file diff --git a/src/docs/let_underscore_drop.txt b/src/docs/let_underscore_drop.txt deleted file mode 100644 index 29ce9bf50ce6..000000000000 --- a/src/docs/let_underscore_drop.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for `let _ = ` -where expr has a type that implements `Drop` - -### Why is this bad? -This statement immediately drops the initializer -expression instead of extending its lifetime to the end of the scope, which -is often not intended. To extend the expression's lifetime to the end of the -scope, use an underscore-prefixed name instead (i.e. _var). If you want to -explicitly drop the expression, `std::mem::drop` conveys your intention -better and is less error-prone. - -### Example -``` -{ - let _ = DroppableItem; - // ^ dropped here - /* more code */ -} -``` - -Use instead: -``` -{ - let _droppable = DroppableItem; - /* more code */ - // dropped at end of scope -} -``` \ No newline at end of file diff --git a/src/docs/let_underscore_lock.txt b/src/docs/let_underscore_lock.txt deleted file mode 100644 index bd8217fb58b3..000000000000 --- a/src/docs/let_underscore_lock.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for `let _ = sync_lock`. -This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`. - -### Why is this bad? -This statement immediately drops the lock instead of -extending its lifetime to the end of the scope, which is often not intended. -To extend lock lifetime to the end of the scope, use an underscore-prefixed -name instead (i.e. _lock). If you want to explicitly drop the lock, -`std::mem::drop` conveys your intention better and is less error-prone. - -### Example -``` -let _ = mutex.lock(); -``` - -Use instead: -``` -let _lock = mutex.lock(); -``` \ No newline at end of file diff --git a/src/docs/let_underscore_must_use.txt b/src/docs/let_underscore_must_use.txt deleted file mode 100644 index 270b81d9a4c4..000000000000 --- a/src/docs/let_underscore_must_use.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for `let _ = ` where expr is `#[must_use]` - -### Why is this bad? -It's better to explicitly handle the value of a `#[must_use]` -expr - -### Example -``` -fn f() -> Result { - Ok(0) -} - -let _ = f(); -// is_ok() is marked #[must_use] -let _ = f().is_ok(); -``` \ No newline at end of file diff --git a/src/docs/let_unit_value.txt b/src/docs/let_unit_value.txt deleted file mode 100644 index bc16d5b3d81b..000000000000 --- a/src/docs/let_unit_value.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for binding a unit value. - -### Why is this bad? -A unit value cannot usefully be used anywhere. So -binding one is kind of pointless. - -### Example -``` -let x = { - 1; -}; -``` \ No newline at end of file diff --git a/src/docs/linkedlist.txt b/src/docs/linkedlist.txt deleted file mode 100644 index 986ff1369e3c..000000000000 --- a/src/docs/linkedlist.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for usage of any `LinkedList`, suggesting to use a -`Vec` or a `VecDeque` (formerly called `RingBuf`). - -### Why is this bad? -Gankro says: - -> The TL;DR of `LinkedList` is that it's built on a massive amount of -pointers and indirection. -> It wastes memory, it has terrible cache locality, and is all-around slow. -`RingBuf`, while -> "only" amortized for push/pop, should be faster in the general case for -almost every possible -> workload, and isn't even amortized at all if you can predict the capacity -you need. -> -> `LinkedList`s are only really good if you're doing a lot of merging or -splitting of lists. -> This is because they can just mangle some pointers instead of actually -copying the data. Even -> if you're doing a lot of insertion in the middle of the list, `RingBuf` -can still be better -> because of how expensive it is to seek to the middle of a `LinkedList`. - -### Known problems -False positives – the instances where using a -`LinkedList` makes sense are few and far between, but they can still happen. - -### Example -``` -let x: LinkedList = LinkedList::new(); -``` \ No newline at end of file diff --git a/src/docs/lossy_float_literal.txt b/src/docs/lossy_float_literal.txt deleted file mode 100644 index bbcb9115ea62..000000000000 --- a/src/docs/lossy_float_literal.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for whole number float literals that -cannot be represented as the underlying type without loss. - -### Why is this bad? -Rust will silently lose precision during -conversion to a float. - -### Example -``` -let _: f32 = 16_777_217.0; // 16_777_216.0 -``` - -Use instead: -``` -let _: f32 = 16_777_216.0; -let _: f64 = 16_777_217.0; -``` \ No newline at end of file diff --git a/src/docs/macro_use_imports.txt b/src/docs/macro_use_imports.txt deleted file mode 100644 index 6a8180a60bc6..000000000000 --- a/src/docs/macro_use_imports.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for `#[macro_use] use...`. - -### Why is this bad? -Since the Rust 2018 edition you can import -macro's directly, this is considered idiomatic. - -### Example -``` -#[macro_use] -use some_macro; -``` \ No newline at end of file diff --git a/src/docs/main_recursion.txt b/src/docs/main_recursion.txt deleted file mode 100644 index e49becd15bbd..000000000000 --- a/src/docs/main_recursion.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for recursion using the entrypoint. - -### Why is this bad? -Apart from special setups (which we could detect following attributes like #![no_std]), -recursing into main() seems like an unintuitive anti-pattern we should be able to detect. - -### Example -``` -fn main() { - main(); -} -``` \ No newline at end of file diff --git a/src/docs/manual_assert.txt b/src/docs/manual_assert.txt deleted file mode 100644 index 93653081a2ce..000000000000 --- a/src/docs/manual_assert.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Detects `if`-then-`panic!` that can be replaced with `assert!`. - -### Why is this bad? -`assert!` is simpler than `if`-then-`panic!`. - -### Example -``` -let sad_people: Vec<&str> = vec![]; -if !sad_people.is_empty() { - panic!("there are sad people: {:?}", sad_people); -} -``` -Use instead: -``` -let sad_people: Vec<&str> = vec![]; -assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people); -``` \ No newline at end of file diff --git a/src/docs/manual_async_fn.txt b/src/docs/manual_async_fn.txt deleted file mode 100644 index d01ac402e0d2..000000000000 --- a/src/docs/manual_async_fn.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -It checks for manual implementations of `async` functions. - -### Why is this bad? -It's more idiomatic to use the dedicated syntax. - -### Example -``` -use std::future::Future; - -fn foo() -> impl Future { async { 42 } } -``` -Use instead: -``` -async fn foo() -> i32 { 42 } -``` \ No newline at end of file diff --git a/src/docs/manual_bits.txt b/src/docs/manual_bits.txt deleted file mode 100644 index b96c2eb151d4..000000000000 --- a/src/docs/manual_bits.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for uses of `std::mem::size_of::() * 8` when -`T::BITS` is available. - -### Why is this bad? -Can be written as the shorter `T::BITS`. - -### Example -``` -std::mem::size_of::() * 8; -``` -Use instead: -``` -usize::BITS as usize; -``` \ No newline at end of file diff --git a/src/docs/manual_clamp.txt b/src/docs/manual_clamp.txt deleted file mode 100644 index 8993f6683adf..000000000000 --- a/src/docs/manual_clamp.txt +++ /dev/null @@ -1,46 +0,0 @@ -### What it does -Identifies good opportunities for a clamp function from std or core, and suggests using it. - -### Why is this bad? -clamp is much shorter, easier to read, and doesn't use any control flow. - -### Known issue(s) -If the clamped variable is NaN this suggestion will cause the code to propagate NaN -rather than returning either `max` or `min`. - -`clamp` functions will panic if `max < min`, `max.is_nan()`, or `min.is_nan()`. -Some may consider panicking in these situations to be desirable, but it also may -introduce panicking where there wasn't any before. - -### Examples -``` -if input > max { - max -} else if input < min { - min -} else { - input -} -``` - -``` -input.max(min).min(max) -``` - -``` -match input { - x if x > max => max, - x if x < min => min, - x => x, -} -``` - -``` -let mut x = input; -if x < min { x = min; } -if x > max { x = max; } -``` -Use instead: -``` -input.clamp(min, max) -``` \ No newline at end of file diff --git a/src/docs/manual_filter.txt b/src/docs/manual_filter.txt deleted file mode 100644 index 19a4d9319d94..000000000000 --- a/src/docs/manual_filter.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for usages of `match` which could be implemented using `filter` - -### Why is this bad? -Using the `filter` method is clearer and more concise. - -### Example -``` -match Some(0) { - Some(x) => if x % 2 == 0 { - Some(x) - } else { - None - }, - None => None, -}; -``` -Use instead: -``` -Some(0).filter(|&x| x % 2 == 0); -``` \ No newline at end of file diff --git a/src/docs/manual_filter_map.txt b/src/docs/manual_filter_map.txt deleted file mode 100644 index 3b6860798ff5..000000000000 --- a/src/docs/manual_filter_map.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `_.filter(_).map(_)` that can be written more simply -as `filter_map(_)`. - -### Why is this bad? -Redundant code in the `filter` and `map` operations is poor style and -less performant. - -### Example -``` -(0_i32..10) - .filter(|n| n.checked_add(1).is_some()) - .map(|n| n.checked_add(1).unwrap()); -``` - -Use instead: -``` -(0_i32..10).filter_map(|n| n.checked_add(1)); -``` \ No newline at end of file diff --git a/src/docs/manual_find.txt b/src/docs/manual_find.txt deleted file mode 100644 index e3e07a2771f9..000000000000 --- a/src/docs/manual_find.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Check for manual implementations of Iterator::find - -### Why is this bad? -It doesn't affect performance, but using `find` is shorter and easier to read. - -### Example - -``` -fn example(arr: Vec) -> Option { - for el in arr { - if el == 1 { - return Some(el); - } - } - None -} -``` -Use instead: -``` -fn example(arr: Vec) -> Option { - arr.into_iter().find(|&el| el == 1) -} -``` \ No newline at end of file diff --git a/src/docs/manual_find_map.txt b/src/docs/manual_find_map.txt deleted file mode 100644 index 83b22060c0e1..000000000000 --- a/src/docs/manual_find_map.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `_.find(_).map(_)` that can be written more simply -as `find_map(_)`. - -### Why is this bad? -Redundant code in the `find` and `map` operations is poor style and -less performant. - -### Example -``` -(0_i32..10) - .find(|n| n.checked_add(1).is_some()) - .map(|n| n.checked_add(1).unwrap()); -``` - -Use instead: -``` -(0_i32..10).find_map(|n| n.checked_add(1)); -``` \ No newline at end of file diff --git a/src/docs/manual_flatten.txt b/src/docs/manual_flatten.txt deleted file mode 100644 index 62d5f3ec9357..000000000000 --- a/src/docs/manual_flatten.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Check for unnecessary `if let` usage in a for loop -where only the `Some` or `Ok` variant of the iterator element is used. - -### Why is this bad? -It is verbose and can be simplified -by first calling the `flatten` method on the `Iterator`. - -### Example - -``` -let x = vec![Some(1), Some(2), Some(3)]; -for n in x { - if let Some(n) = n { - println!("{}", n); - } -} -``` -Use instead: -``` -let x = vec![Some(1), Some(2), Some(3)]; -for n in x.into_iter().flatten() { - println!("{}", n); -} -``` \ No newline at end of file diff --git a/src/docs/manual_instant_elapsed.txt b/src/docs/manual_instant_elapsed.txt deleted file mode 100644 index dde3d493c70d..000000000000 --- a/src/docs/manual_instant_elapsed.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Lints subtraction between `Instant::now()` and another `Instant`. - -### Why is this bad? -It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns -as `Instant` subtraction saturates. - -`prev_instant.elapsed()` also more clearly signals intention. - -### Example -``` -use std::time::Instant; -let prev_instant = Instant::now(); -let duration = Instant::now() - prev_instant; -``` -Use instead: -``` -use std::time::Instant; -let prev_instant = Instant::now(); -let duration = prev_instant.elapsed(); -``` \ No newline at end of file diff --git a/src/docs/manual_map.txt b/src/docs/manual_map.txt deleted file mode 100644 index 7f68ccd1037e..000000000000 --- a/src/docs/manual_map.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for usages of `match` which could be implemented using `map` - -### Why is this bad? -Using the `map` method is clearer and more concise. - -### Example -``` -match Some(0) { - Some(x) => Some(x + 1), - None => None, -}; -``` -Use instead: -``` -Some(0).map(|x| x + 1); -``` \ No newline at end of file diff --git a/src/docs/manual_memcpy.txt b/src/docs/manual_memcpy.txt deleted file mode 100644 index d7690bf25866..000000000000 --- a/src/docs/manual_memcpy.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for for-loops that manually copy items between -slices that could be optimized by having a memcpy. - -### Why is this bad? -It is not as fast as a memcpy. - -### Example -``` -for i in 0..src.len() { - dst[i + 64] = src[i]; -} -``` - -Use instead: -``` -dst[64..(src.len() + 64)].clone_from_slice(&src[..]); -``` \ No newline at end of file diff --git a/src/docs/manual_non_exhaustive.txt b/src/docs/manual_non_exhaustive.txt deleted file mode 100644 index fb021393bd7c..000000000000 --- a/src/docs/manual_non_exhaustive.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Checks for manual implementations of the non-exhaustive pattern. - -### Why is this bad? -Using the #[non_exhaustive] attribute expresses better the intent -and allows possible optimizations when applied to enums. - -### Example -``` -struct S { - pub a: i32, - pub b: i32, - _c: (), -} - -enum E { - A, - B, - #[doc(hidden)] - _C, -} - -struct T(pub i32, pub i32, ()); -``` -Use instead: -``` -#[non_exhaustive] -struct S { - pub a: i32, - pub b: i32, -} - -#[non_exhaustive] -enum E { - A, - B, -} - -#[non_exhaustive] -struct T(pub i32, pub i32); -``` \ No newline at end of file diff --git a/src/docs/manual_ok_or.txt b/src/docs/manual_ok_or.txt deleted file mode 100644 index 5accdf25965a..000000000000 --- a/src/docs/manual_ok_or.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does - -Finds patterns that reimplement `Option::ok_or`. - -### Why is this bad? - -Concise code helps focusing on behavior instead of boilerplate. - -### Examples -``` -let foo: Option = None; -foo.map_or(Err("error"), |v| Ok(v)); -``` - -Use instead: -``` -let foo: Option = None; -foo.ok_or("error"); -``` \ No newline at end of file diff --git a/src/docs/manual_range_contains.txt b/src/docs/manual_range_contains.txt deleted file mode 100644 index 0ade26951d38..000000000000 --- a/src/docs/manual_range_contains.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for expressions like `x >= 3 && x < 8` that could -be more readably expressed as `(3..8).contains(x)`. - -### Why is this bad? -`contains` expresses the intent better and has less -failure modes (such as fencepost errors or using `||` instead of `&&`). - -### Example -``` -// given -let x = 6; - -assert!(x >= 3 && x < 8); -``` -Use instead: -``` -assert!((3..8).contains(&x)); -``` \ No newline at end of file diff --git a/src/docs/manual_rem_euclid.txt b/src/docs/manual_rem_euclid.txt deleted file mode 100644 index d3bb8c61304e..000000000000 --- a/src/docs/manual_rem_euclid.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for an expression like `((x % 4) + 4) % 4` which is a common manual reimplementation -of `x.rem_euclid(4)`. - -### Why is this bad? -It's simpler and more readable. - -### Example -``` -let x: i32 = 24; -let rem = ((x % 4) + 4) % 4; -``` -Use instead: -``` -let x: i32 = 24; -let rem = x.rem_euclid(4); -``` \ No newline at end of file diff --git a/src/docs/manual_retain.txt b/src/docs/manual_retain.txt deleted file mode 100644 index cd4f65a93fc3..000000000000 --- a/src/docs/manual_retain.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for code to be replaced by `.retain()`. -### Why is this bad? -`.retain()` is simpler and avoids needless allocation. -### Example -``` -let mut vec = vec![0, 1, 2]; -vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); -vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); -``` -Use instead: -``` -let mut vec = vec![0, 1, 2]; -vec.retain(|x| x % 2 == 0); -``` \ No newline at end of file diff --git a/src/docs/manual_saturating_arithmetic.txt b/src/docs/manual_saturating_arithmetic.txt deleted file mode 100644 index d9f5d3d11871..000000000000 --- a/src/docs/manual_saturating_arithmetic.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`. - -### Why is this bad? -These can be written simply with `saturating_add/sub` methods. - -### Example -``` -let add = x.checked_add(y).unwrap_or(u32::MAX); -let sub = x.checked_sub(y).unwrap_or(u32::MIN); -``` - -can be written using dedicated methods for saturating addition/subtraction as: - -``` -let add = x.saturating_add(y); -let sub = x.saturating_sub(y); -``` \ No newline at end of file diff --git a/src/docs/manual_split_once.txt b/src/docs/manual_split_once.txt deleted file mode 100644 index 291ae447de08..000000000000 --- a/src/docs/manual_split_once.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for usages of `str::splitn(2, _)` - -### Why is this bad? -`split_once` is both clearer in intent and slightly more efficient. - -### Example -``` -let s = "key=value=add"; -let (key, value) = s.splitn(2, '=').next_tuple()?; -let value = s.splitn(2, '=').nth(1)?; - -let mut parts = s.splitn(2, '='); -let key = parts.next()?; -let value = parts.next()?; -``` - -Use instead: -``` -let s = "key=value=add"; -let (key, value) = s.split_once('=')?; -let value = s.split_once('=')?.1; - -let (key, value) = s.split_once('=')?; -``` - -### Limitations -The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()` -in two separate `let` statements that immediately follow the `splitn()` \ No newline at end of file diff --git a/src/docs/manual_str_repeat.txt b/src/docs/manual_str_repeat.txt deleted file mode 100644 index 1d4a7a48e203..000000000000 --- a/src/docs/manual_str_repeat.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for manual implementations of `str::repeat` - -### Why is this bad? -These are both harder to read, as well as less performant. - -### Example -``` -let x: String = std::iter::repeat('x').take(10).collect(); -``` - -Use instead: -``` -let x: String = "x".repeat(10); -``` \ No newline at end of file diff --git a/src/docs/manual_string_new.txt b/src/docs/manual_string_new.txt deleted file mode 100644 index 4cbc43f8f843..000000000000 --- a/src/docs/manual_string_new.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does - -Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`, -`String::from("")` and others. - -### Why is this bad? - -Different ways of creating an empty string makes your code less standardized, which can -be confusing. - -### Example -``` -let a = "".to_string(); -let b: String = "".into(); -``` -Use instead: -``` -let a = String::new(); -let b = String::new(); -``` \ No newline at end of file diff --git a/src/docs/manual_strip.txt b/src/docs/manual_strip.txt deleted file mode 100644 index f32d8e7a09bb..000000000000 --- a/src/docs/manual_strip.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using -the pattern's length. - -### Why is this bad? -Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no -slicing which may panic and the compiler does not need to insert this panic code. It is -also sometimes more readable as it removes the need for duplicating or storing the pattern -used by `str::{starts,ends}_with` and in the slicing. - -### Example -``` -let s = "hello, world!"; -if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); -} -``` -Use instead: -``` -let s = "hello, world!"; -if let Some(end) = s.strip_prefix("hello, ") { - assert_eq!(end.to_uppercase(), "WORLD!"); -} -``` \ No newline at end of file diff --git a/src/docs/manual_swap.txt b/src/docs/manual_swap.txt deleted file mode 100644 index bd9526288e35..000000000000 --- a/src/docs/manual_swap.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for manual swapping. - -### Why is this bad? -The `std::mem::swap` function exposes the intent better -without deinitializing or copying either variable. - -### Example -``` -let mut a = 42; -let mut b = 1337; - -let t = b; -b = a; -a = t; -``` -Use std::mem::swap(): -``` -let mut a = 1; -let mut b = 2; -std::mem::swap(&mut a, &mut b); -``` \ No newline at end of file diff --git a/src/docs/manual_unwrap_or.txt b/src/docs/manual_unwrap_or.txt deleted file mode 100644 index 1fd7d831bfca..000000000000 --- a/src/docs/manual_unwrap_or.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. - -### Why is this bad? -Concise code helps focusing on behavior instead of boilerplate. - -### Example -``` -let foo: Option = None; -match foo { - Some(v) => v, - None => 1, -}; -``` - -Use instead: -``` -let foo: Option = None; -foo.unwrap_or(1); -``` \ No newline at end of file diff --git a/src/docs/many_single_char_names.txt b/src/docs/many_single_char_names.txt deleted file mode 100644 index 55ee5da55574..000000000000 --- a/src/docs/many_single_char_names.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for too many variables whose name consists of a -single character. - -### Why is this bad? -It's hard to memorize what a variable means without a -descriptive name. - -### Example -``` -let (a, b, c, d, e, f, g) = (...); -``` \ No newline at end of file diff --git a/src/docs/map_clone.txt b/src/docs/map_clone.txt deleted file mode 100644 index 3ee27f072ef3..000000000000 --- a/src/docs/map_clone.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `map(|x| x.clone())` or -dereferencing closures for `Copy` types, on `Iterator` or `Option`, -and suggests `cloned()` or `copied()` instead - -### Why is this bad? -Readability, this can be written more concisely - -### Example -``` -let x = vec![42, 43]; -let y = x.iter(); -let z = y.map(|i| *i); -``` - -The correct use would be: - -``` -let x = vec![42, 43]; -let y = x.iter(); -let z = y.cloned(); -``` \ No newline at end of file diff --git a/src/docs/map_collect_result_unit.txt b/src/docs/map_collect_result_unit.txt deleted file mode 100644 index 9b720612495c..000000000000 --- a/src/docs/map_collect_result_unit.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for usage of `_.map(_).collect::()`. - -### Why is this bad? -Using `try_for_each` instead is more readable and idiomatic. - -### Example -``` -(0..3).map(|t| Err(t)).collect::>(); -``` -Use instead: -``` -(0..3).try_for_each(|t| Err(t)); -``` \ No newline at end of file diff --git a/src/docs/map_entry.txt b/src/docs/map_entry.txt deleted file mode 100644 index 20dba1798d0d..000000000000 --- a/src/docs/map_entry.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for uses of `contains_key` + `insert` on `HashMap` -or `BTreeMap`. - -### Why is this bad? -Using `entry` is more efficient. - -### Known problems -The suggestion may have type inference errors in some cases. e.g. -``` -let mut map = std::collections::HashMap::new(); -let _ = if !map.contains_key(&0) { - map.insert(0, 0) -} else { - None -}; -``` - -### Example -``` -if !map.contains_key(&k) { - map.insert(k, v); -} -``` -Use instead: -``` -map.entry(k).or_insert(v); -``` \ No newline at end of file diff --git a/src/docs/map_err_ignore.txt b/src/docs/map_err_ignore.txt deleted file mode 100644 index 2606c13a7afd..000000000000 --- a/src/docs/map_err_ignore.txt +++ /dev/null @@ -1,93 +0,0 @@ -### What it does -Checks for instances of `map_err(|_| Some::Enum)` - -### Why is this bad? -This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error - -### Example -Before: -``` -use std::fmt; - -#[derive(Debug)] -enum Error { - Indivisible, - Remainder(u8), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::Indivisible => write!(f, "could not divide input by three"), - Error::Remainder(remainder) => write!( - f, - "input is not divisible by three, remainder = {}", - remainder - ), - } - } -} - -impl std::error::Error for Error {} - -fn divisible_by_3(input: &str) -> Result<(), Error> { - input - .parse::() - .map_err(|_| Error::Indivisible) - .map(|v| v % 3) - .and_then(|remainder| { - if remainder == 0 { - Ok(()) - } else { - Err(Error::Remainder(remainder as u8)) - } - }) -} - ``` - - After: - ```rust -use std::{fmt, num::ParseIntError}; - -#[derive(Debug)] -enum Error { - Indivisible(ParseIntError), - Remainder(u8), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::Indivisible(_) => write!(f, "could not divide input by three"), - Error::Remainder(remainder) => write!( - f, - "input is not divisible by three, remainder = {}", - remainder - ), - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::Indivisible(source) => Some(source), - _ => None, - } - } -} - -fn divisible_by_3(input: &str) -> Result<(), Error> { - input - .parse::() - .map_err(Error::Indivisible) - .map(|v| v % 3) - .and_then(|remainder| { - if remainder == 0 { - Ok(()) - } else { - Err(Error::Remainder(remainder as u8)) - } - }) -} -``` \ No newline at end of file diff --git a/src/docs/map_flatten.txt b/src/docs/map_flatten.txt deleted file mode 100644 index 73c0e51407f2..000000000000 --- a/src/docs/map_flatten.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option` - -### Why is this bad? -Readability, this can be written more concisely as -`_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option` - -### Example -``` -let vec = vec![vec![1]]; -let opt = Some(5); - -vec.iter().map(|x| x.iter()).flatten(); -opt.map(|x| Some(x * 2)).flatten(); -``` - -Use instead: -``` -vec.iter().flat_map(|x| x.iter()); -opt.and_then(|x| Some(x * 2)); -``` \ No newline at end of file diff --git a/src/docs/map_identity.txt b/src/docs/map_identity.txt deleted file mode 100644 index e2e7af0bed9b..000000000000 --- a/src/docs/map_identity.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for instances of `map(f)` where `f` is the identity function. - -### Why is this bad? -It can be written more concisely without the call to `map`. - -### Example -``` -let x = [1, 2, 3]; -let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect(); -``` -Use instead: -``` -let x = [1, 2, 3]; -let y: Vec<_> = x.iter().map(|x| 2*x).collect(); -``` \ No newline at end of file diff --git a/src/docs/map_unwrap_or.txt b/src/docs/map_unwrap_or.txt deleted file mode 100644 index 485b29f01b10..000000000000 --- a/src/docs/map_unwrap_or.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or -`result.map(_).unwrap_or_else(_)`. - -### Why is this bad? -Readability, these can be written more concisely (resp.) as -`option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`. - -### Known problems -The order of the arguments is not in execution order - -### Examples -``` -option.map(|a| a + 1).unwrap_or(0); -result.map(|a| a + 1).unwrap_or_else(some_function); -``` - -Use instead: -``` -option.map_or(0, |a| a + 1); -result.map_or_else(some_function, |a| a + 1); -``` \ No newline at end of file diff --git a/src/docs/match_as_ref.txt b/src/docs/match_as_ref.txt deleted file mode 100644 index 5e5f3d645a4e..000000000000 --- a/src/docs/match_as_ref.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for match which is used to add a reference to an -`Option` value. - -### Why is this bad? -Using `as_ref()` or `as_mut()` instead is shorter. - -### Example -``` -let x: Option<()> = None; - -let r: Option<&()> = match x { - None => None, - Some(ref v) => Some(v), -}; -``` - -Use instead: -``` -let x: Option<()> = None; - -let r: Option<&()> = x.as_ref(); -``` \ No newline at end of file diff --git a/src/docs/match_bool.txt b/src/docs/match_bool.txt deleted file mode 100644 index 96f9e1f8b7d1..000000000000 --- a/src/docs/match_bool.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for matches where match expression is a `bool`. It -suggests to replace the expression with an `if...else` block. - -### Why is this bad? -It makes the code less readable. - -### Example -``` -let condition: bool = true; -match condition { - true => foo(), - false => bar(), -} -``` -Use if/else instead: -``` -let condition: bool = true; -if condition { - foo(); -} else { - bar(); -} -``` \ No newline at end of file diff --git a/src/docs/match_like_matches_macro.txt b/src/docs/match_like_matches_macro.txt deleted file mode 100644 index 643e2ddc97ba..000000000000 --- a/src/docs/match_like_matches_macro.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for `match` or `if let` expressions producing a -`bool` that could be written using `matches!` - -### Why is this bad? -Readability and needless complexity. - -### Known problems -This lint falsely triggers, if there are arms with -`cfg` attributes that remove an arm evaluating to `false`. - -### Example -``` -let x = Some(5); - -let a = match x { - Some(0) => true, - _ => false, -}; - -let a = if let Some(0) = x { - true -} else { - false -}; -``` - -Use instead: -``` -let x = Some(5); -let a = matches!(x, Some(0)); -``` \ No newline at end of file diff --git a/src/docs/match_on_vec_items.txt b/src/docs/match_on_vec_items.txt deleted file mode 100644 index 981d18d0f9ed..000000000000 --- a/src/docs/match_on_vec_items.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for `match vec[idx]` or `match vec[n..m]`. - -### Why is this bad? -This can panic at runtime. - -### Example -``` -let arr = vec![0, 1, 2, 3]; -let idx = 1; - -match arr[idx] { - 0 => println!("{}", 0), - 1 => println!("{}", 3), - _ => {}, -} -``` - -Use instead: -``` -let arr = vec![0, 1, 2, 3]; -let idx = 1; - -match arr.get(idx) { - Some(0) => println!("{}", 0), - Some(1) => println!("{}", 3), - _ => {}, -} -``` \ No newline at end of file diff --git a/src/docs/match_overlapping_arm.txt b/src/docs/match_overlapping_arm.txt deleted file mode 100644 index 841c091bd5ca..000000000000 --- a/src/docs/match_overlapping_arm.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for overlapping match arms. - -### Why is this bad? -It is likely to be an error and if not, makes the code -less obvious. - -### Example -``` -let x = 5; -match x { - 1..=10 => println!("1 ... 10"), - 5..=15 => println!("5 ... 15"), - _ => (), -} -``` \ No newline at end of file diff --git a/src/docs/match_ref_pats.txt b/src/docs/match_ref_pats.txt deleted file mode 100644 index b1d902995097..000000000000 --- a/src/docs/match_ref_pats.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for matches where all arms match a reference, -suggesting to remove the reference and deref the matched expression -instead. It also checks for `if let &foo = bar` blocks. - -### Why is this bad? -It just makes the code less readable. That reference -destructuring adds nothing to the code. - -### Example -``` -match x { - &A(ref y) => foo(y), - &B => bar(), - _ => frob(&x), -} -``` - -Use instead: -``` -match *x { - A(ref y) => foo(y), - B => bar(), - _ => frob(x), -} -``` \ No newline at end of file diff --git a/src/docs/match_result_ok.txt b/src/docs/match_result_ok.txt deleted file mode 100644 index eea7c8e00f1b..000000000000 --- a/src/docs/match_result_ok.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for unnecessary `ok()` in `while let`. - -### Why is this bad? -Calling `ok()` in `while let` is unnecessary, instead match -on `Ok(pat)` - -### Example -``` -while let Some(value) = iter.next().ok() { - vec.push(value) -} - -if let Some(value) = iter.next().ok() { - vec.push(value) -} -``` -Use instead: -``` -while let Ok(value) = iter.next() { - vec.push(value) -} - -if let Ok(value) = iter.next() { - vec.push(value) -} -``` \ No newline at end of file diff --git a/src/docs/match_same_arms.txt b/src/docs/match_same_arms.txt deleted file mode 100644 index 14edf12032e0..000000000000 --- a/src/docs/match_same_arms.txt +++ /dev/null @@ -1,38 +0,0 @@ -### What it does -Checks for `match` with identical arm bodies. - -### Why is this bad? -This is probably a copy & paste error. If arm bodies -are the same on purpose, you can factor them -[using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). - -### Known problems -False positive possible with order dependent `match` -(see issue -[#860](https://github.com/rust-lang/rust-clippy/issues/860)). - -### Example -``` -match foo { - Bar => bar(), - Quz => quz(), - Baz => bar(), // <= oops -} -``` - -This should probably be -``` -match foo { - Bar => bar(), - Quz => quz(), - Baz => baz(), // <= fixed -} -``` - -or if the original code was not a typo: -``` -match foo { - Bar | Baz => bar(), // <= shows the intent better - Quz => quz(), -} -``` \ No newline at end of file diff --git a/src/docs/match_single_binding.txt b/src/docs/match_single_binding.txt deleted file mode 100644 index 67ded0bbd553..000000000000 --- a/src/docs/match_single_binding.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for useless match that binds to only one value. - -### Why is this bad? -Readability and needless complexity. - -### Known problems - Suggested replacements may be incorrect when `match` -is actually binding temporary value, bringing a 'dropped while borrowed' error. - -### Example -``` -match (a, b) { - (c, d) => { - // useless match - } -} -``` - -Use instead: -``` -let (c, d) = (a, b); -``` \ No newline at end of file diff --git a/src/docs/match_str_case_mismatch.txt b/src/docs/match_str_case_mismatch.txt deleted file mode 100644 index 19e74c2084ea..000000000000 --- a/src/docs/match_str_case_mismatch.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for `match` expressions modifying the case of a string with non-compliant arms - -### Why is this bad? -The arm is unreachable, which is likely a mistake - -### Example -``` -match &*text.to_ascii_lowercase() { - "foo" => {}, - "Bar" => {}, - _ => {}, -} -``` -Use instead: -``` -match &*text.to_ascii_lowercase() { - "foo" => {}, - "bar" => {}, - _ => {}, -} -``` \ No newline at end of file diff --git a/src/docs/match_wild_err_arm.txt b/src/docs/match_wild_err_arm.txt deleted file mode 100644 index f89b3a23a1ca..000000000000 --- a/src/docs/match_wild_err_arm.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for arm which matches all errors with `Err(_)` -and take drastic actions like `panic!`. - -### Why is this bad? -It is generally a bad practice, similar to -catching all exceptions in java with `catch(Exception)` - -### Example -``` -let x: Result = Ok(3); -match x { - Ok(_) => println!("ok"), - Err(_) => panic!("err"), -} -``` \ No newline at end of file diff --git a/src/docs/match_wildcard_for_single_variants.txt b/src/docs/match_wildcard_for_single_variants.txt deleted file mode 100644 index 25559b9ecdcf..000000000000 --- a/src/docs/match_wildcard_for_single_variants.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for wildcard enum matches for a single variant. - -### Why is this bad? -New enum variants added by library updates can be missed. - -### Known problems -Suggested replacements may not use correct path to enum -if it's not present in the current scope. - -### Example -``` -match x { - Foo::A => {}, - Foo::B => {}, - _ => {}, -} -``` - -Use instead: -``` -match x { - Foo::A => {}, - Foo::B => {}, - Foo::C => {}, -} -``` \ No newline at end of file diff --git a/src/docs/maybe_infinite_iter.txt b/src/docs/maybe_infinite_iter.txt deleted file mode 100644 index 1204a49b4668..000000000000 --- a/src/docs/maybe_infinite_iter.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for iteration that may be infinite. - -### Why is this bad? -While there may be places where this is acceptable -(e.g., in event streams), in most cases this is simply an error. - -### Known problems -The code may have a condition to stop iteration, but -this lint is not clever enough to analyze it. - -### Example -``` -let infinite_iter = 0..; -[0..].iter().zip(infinite_iter.take_while(|x| *x > 5)); -``` \ No newline at end of file diff --git a/src/docs/mem_forget.txt b/src/docs/mem_forget.txt deleted file mode 100644 index a6888c48fc33..000000000000 --- a/src/docs/mem_forget.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for usage of `std::mem::forget(t)` where `t` is -`Drop`. - -### Why is this bad? -`std::mem::forget(t)` prevents `t` from running its -destructor, possibly causing leaks. - -### Example -``` -mem::forget(Rc::new(55)) -``` \ No newline at end of file diff --git a/src/docs/mem_replace_option_with_none.txt b/src/docs/mem_replace_option_with_none.txt deleted file mode 100644 index 7f243d1c1656..000000000000 --- a/src/docs/mem_replace_option_with_none.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for `mem::replace()` on an `Option` with -`None`. - -### Why is this bad? -`Option` already has the method `take()` for -taking its current value (Some(..) or None) and replacing it with -`None`. - -### Example -``` -use std::mem; - -let mut an_option = Some(0); -let replaced = mem::replace(&mut an_option, None); -``` -Is better expressed with: -``` -let mut an_option = Some(0); -let taken = an_option.take(); -``` \ No newline at end of file diff --git a/src/docs/mem_replace_with_default.txt b/src/docs/mem_replace_with_default.txt deleted file mode 100644 index 24e0913a30c9..000000000000 --- a/src/docs/mem_replace_with_default.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for `std::mem::replace` on a value of type -`T` with `T::default()`. - -### Why is this bad? -`std::mem` module already has the method `take` to -take the current value and replace it with the default value of that type. - -### Example -``` -let mut text = String::from("foo"); -let replaced = std::mem::replace(&mut text, String::default()); -``` -Is better expressed with: -``` -let mut text = String::from("foo"); -let taken = std::mem::take(&mut text); -``` \ No newline at end of file diff --git a/src/docs/mem_replace_with_uninit.txt b/src/docs/mem_replace_with_uninit.txt deleted file mode 100644 index 0bb483668abc..000000000000 --- a/src/docs/mem_replace_with_uninit.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for `mem::replace(&mut _, mem::uninitialized())` -and `mem::replace(&mut _, mem::zeroed())`. - -### Why is this bad? -This will lead to undefined behavior even if the -value is overwritten later, because the uninitialized value may be -observed in the case of a panic. - -### Example -``` -use std::mem; - -#[allow(deprecated, invalid_value)] -fn myfunc (v: &mut Vec) { - let taken_v = unsafe { mem::replace(v, mem::uninitialized()) }; - let new_v = may_panic(taken_v); // undefined behavior on panic - mem::forget(mem::replace(v, new_v)); -} -``` - -The [take_mut](https://docs.rs/take_mut) crate offers a sound solution, -at the cost of either lazily creating a replacement value or aborting -on panic, to ensure that the uninitialized value cannot be observed. \ No newline at end of file diff --git a/src/docs/min_max.txt b/src/docs/min_max.txt deleted file mode 100644 index 6acf0f932e98..000000000000 --- a/src/docs/min_max.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for expressions where `std::cmp::min` and `max` are -used to clamp values, but switched so that the result is constant. - -### Why is this bad? -This is in all probability not the intended outcome. At -the least it hurts readability of the code. - -### Example -``` -min(0, max(100, x)) - -// or - -x.max(100).min(0) -``` -It will always be equal to `0`. Probably the author meant to clamp the value -between 0 and 100, but has erroneously swapped `min` and `max`. \ No newline at end of file diff --git a/src/docs/mismatched_target_os.txt b/src/docs/mismatched_target_os.txt deleted file mode 100644 index 51e5ec6e7c5c..000000000000 --- a/src/docs/mismatched_target_os.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for cfg attributes having operating systems used in target family position. - -### Why is this bad? -The configuration option will not be recognised and the related item will not be included -by the conditional compilation engine. - -### Example -``` -#[cfg(linux)] -fn conditional() { } -``` - -Use instead: -``` -#[cfg(target_os = "linux")] -fn conditional() { } - -// or - -#[cfg(unix)] -fn conditional() { } -``` -Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details. \ No newline at end of file diff --git a/src/docs/mismatching_type_param_order.txt b/src/docs/mismatching_type_param_order.txt deleted file mode 100644 index ffc7f32d0aad..000000000000 --- a/src/docs/mismatching_type_param_order.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks for type parameters which are positioned inconsistently between -a type definition and impl block. Specifically, a parameter in an impl -block which has the same name as a parameter in the type def, but is in -a different place. - -### Why is this bad? -Type parameters are determined by their position rather than name. -Naming type parameters inconsistently may cause you to refer to the -wrong type parameter. - -### Limitations -This lint only applies to impl blocks with simple generic params, e.g. -`A`. If there is anything more complicated, such as a tuple, it will be -ignored. - -### Example -``` -struct Foo { - x: A, - y: B, -} -// inside the impl, B refers to Foo::A -impl Foo {} -``` -Use instead: -``` -struct Foo { - x: A, - y: B, -} -impl Foo {} -``` \ No newline at end of file diff --git a/src/docs/misrefactored_assign_op.txt b/src/docs/misrefactored_assign_op.txt deleted file mode 100644 index 3d691fe4178b..000000000000 --- a/src/docs/misrefactored_assign_op.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for `a op= a op b` or `a op= b op a` patterns. - -### Why is this bad? -Most likely these are bugs where one meant to write `a -op= b`. - -### Known problems -Clippy cannot know for sure if `a op= a op b` should have -been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both. -If `a op= a op b` is really the correct behavior it should be -written as `a = a op a op b` as it's less confusing. - -### Example -``` -let mut a = 5; -let b = 2; -// ... -a += a + b; -``` \ No newline at end of file diff --git a/src/docs/missing_const_for_fn.txt b/src/docs/missing_const_for_fn.txt deleted file mode 100644 index 067614d4c46b..000000000000 --- a/src/docs/missing_const_for_fn.txt +++ /dev/null @@ -1,40 +0,0 @@ -### What it does -Suggests the use of `const` in functions and methods where possible. - -### Why is this bad? -Not having the function const prevents callers of the function from being const as well. - -### Known problems -Const functions are currently still being worked on, with some features only being available -on nightly. This lint does not consider all edge cases currently and the suggestions may be -incorrect if you are using this lint on stable. - -Also, the lint only runs one pass over the code. Consider these two non-const functions: - -``` -fn a() -> i32 { - 0 -} -fn b() -> i32 { - a() -} -``` - -When running Clippy, the lint will only suggest to make `a` const, because `b` at this time -can't be const as it calls a non-const function. Making `a` const and running Clippy again, -will suggest to make `b` const, too. - -### Example -``` -fn new() -> Self { - Self { random_number: 42 } -} -``` - -Could be a const fn: - -``` -const fn new() -> Self { - Self { random_number: 42 } -} -``` \ No newline at end of file diff --git a/src/docs/missing_docs_in_private_items.txt b/src/docs/missing_docs_in_private_items.txt deleted file mode 100644 index 5d37505bb171..000000000000 --- a/src/docs/missing_docs_in_private_items.txt +++ /dev/null @@ -1,9 +0,0 @@ -### What it does -Warns if there is missing doc for any documentable item -(public or private). - -### Why is this bad? -Doc is good. *rustc* has a `MISSING_DOCS` -allowed-by-default lint for -public members, but has no way to enforce documentation of private items. -This lint fixes that. \ No newline at end of file diff --git a/src/docs/missing_enforced_import_renames.txt b/src/docs/missing_enforced_import_renames.txt deleted file mode 100644 index 8f4649bd5923..000000000000 --- a/src/docs/missing_enforced_import_renames.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for imports that do not rename the item as specified -in the `enforce-import-renames` config option. - -### Why is this bad? -Consistency is important, if a project has defined import -renames they should be followed. More practically, some item names are too -vague outside of their defining scope this can enforce a more meaningful naming. - -### Example -An example clippy.toml configuration: -``` -enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }] -``` - -``` -use serde_json::Value; -``` -Use instead: -``` -use serde_json::Value as JsonValue; -``` \ No newline at end of file diff --git a/src/docs/missing_errors_doc.txt b/src/docs/missing_errors_doc.txt deleted file mode 100644 index 028778d85aeb..000000000000 --- a/src/docs/missing_errors_doc.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks the doc comments of publicly visible functions that -return a `Result` type and warns if there is no `# Errors` section. - -### Why is this bad? -Documenting the type of errors that can be returned from a -function can help callers write code to handle the errors appropriately. - -### Examples -Since the following function returns a `Result` it has an `# Errors` section in -its doc comment: - -``` -/// # Errors -/// -/// Will return `Err` if `filename` does not exist or the user does not have -/// permission to read it. -pub fn read(filename: String) -> io::Result { - unimplemented!(); -} -``` \ No newline at end of file diff --git a/src/docs/missing_inline_in_public_items.txt b/src/docs/missing_inline_in_public_items.txt deleted file mode 100644 index d90c50fe7f9e..000000000000 --- a/src/docs/missing_inline_in_public_items.txt +++ /dev/null @@ -1,45 +0,0 @@ -### What it does -It lints if an exported function, method, trait method with default impl, -or trait method impl is not `#[inline]`. - -### Why is this bad? -In general, it is not. Functions can be inlined across -crates when that's profitable as long as any form of LTO is used. When LTO is disabled, -functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates -might intend for most of the methods in their public API to be able to be inlined across -crates even when LTO is disabled. For these types of crates, enabling this lint might make -sense. It allows the crate to require all exported methods to be `#[inline]` by default, and -then opt out for specific methods where this might not make sense. - -### Example -``` -pub fn foo() {} // missing #[inline] -fn ok() {} // ok -#[inline] pub fn bar() {} // ok -#[inline(always)] pub fn baz() {} // ok - -pub trait Bar { - fn bar(); // ok - fn def_bar() {} // missing #[inline] -} - -struct Baz; -impl Baz { - fn private() {} // ok -} - -impl Bar for Baz { - fn bar() {} // ok - Baz is not exported -} - -pub struct PubBaz; -impl PubBaz { - fn private() {} // ok - pub fn not_private() {} // missing #[inline] -} - -impl Bar for PubBaz { - fn bar() {} // missing #[inline] - fn def_bar() {} // missing #[inline] -} -``` \ No newline at end of file diff --git a/src/docs/missing_panics_doc.txt b/src/docs/missing_panics_doc.txt deleted file mode 100644 index e5e39a824519..000000000000 --- a/src/docs/missing_panics_doc.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks the doc comments of publicly visible functions that -may panic and warns if there is no `# Panics` section. - -### Why is this bad? -Documenting the scenarios in which panicking occurs -can help callers who do not want to panic to avoid those situations. - -### Examples -Since the following function may panic it has a `# Panics` section in -its doc comment: - -``` -/// # Panics -/// -/// Will panic if y is 0 -pub fn divide_by(x: i32, y: i32) -> i32 { - if y == 0 { - panic!("Cannot divide by 0") - } else { - x / y - } -} -``` \ No newline at end of file diff --git a/src/docs/missing_safety_doc.txt b/src/docs/missing_safety_doc.txt deleted file mode 100644 index 6492eb84f63b..000000000000 --- a/src/docs/missing_safety_doc.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for the doc comments of publicly visible -unsafe functions and warns if there is no `# Safety` section. - -### Why is this bad? -Unsafe functions should document their safety -preconditions, so that users can be sure they are using them safely. - -### Examples -``` -/// This function should really be documented -pub unsafe fn start_apocalypse(u: &mut Universe) { - unimplemented!(); -} -``` - -At least write a line about safety: - -``` -/// # Safety -/// -/// This function should not be called before the horsemen are ready. -pub unsafe fn start_apocalypse(u: &mut Universe) { - unimplemented!(); -} -``` \ No newline at end of file diff --git a/src/docs/missing_spin_loop.txt b/src/docs/missing_spin_loop.txt deleted file mode 100644 index 3a06a91d7183..000000000000 --- a/src/docs/missing_spin_loop.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Check for empty spin loops - -### Why is this bad? -The loop body should have something like `thread::park()` or at least -`std::hint::spin_loop()` to avoid needlessly burning cycles and conserve -energy. Perhaps even better use an actual lock, if possible. - -### Known problems -This lint doesn't currently trigger on `while let` or -`loop { match .. { .. } }` loops, which would be considered idiomatic in -combination with e.g. `AtomicBool::compare_exchange_weak`. - -### Example - -``` -use core::sync::atomic::{AtomicBool, Ordering}; -let b = AtomicBool::new(true); -// give a ref to `b` to another thread,wait for it to become false -while b.load(Ordering::Acquire) {}; -``` -Use instead: -``` -while b.load(Ordering::Acquire) { - std::hint::spin_loop() -} -``` \ No newline at end of file diff --git a/src/docs/missing_trait_methods.txt b/src/docs/missing_trait_methods.txt deleted file mode 100644 index 788ad764f8c3..000000000000 --- a/src/docs/missing_trait_methods.txt +++ /dev/null @@ -1,40 +0,0 @@ -### What it does -Checks if a provided method is used implicitly by a trait -implementation. A usage example would be a wrapper where every method -should perform some operation before delegating to the inner type's -implemenation. - -This lint should typically be enabled on a specific trait `impl` item -rather than globally. - -### Why is this bad? -Indicates that a method is missing. - -### Example -``` -trait Trait { - fn required(); - - fn provided() {} -} - -#[warn(clippy::missing_trait_methods)] -impl Trait for Type { - fn required() { /* ... */ } -} -``` -Use instead: -``` -trait Trait { - fn required(); - - fn provided() {} -} - -#[warn(clippy::missing_trait_methods)] -impl Trait for Type { - fn required() { /* ... */ } - - fn provided() { /* ... */ } -} -``` \ No newline at end of file diff --git a/src/docs/mistyped_literal_suffixes.txt b/src/docs/mistyped_literal_suffixes.txt deleted file mode 100644 index 1760fcbfeacc..000000000000 --- a/src/docs/mistyped_literal_suffixes.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Warns for mistyped suffix in literals - -### Why is this bad? -This is most probably a typo - -### Known problems -- Does not match on integers too large to fit in the corresponding unsigned type -- Does not match on `_127` since that is a valid grouping for decimal and octal numbers - -### Example -``` -`2_32` => `2_i32` -`250_8 => `250_u8` -``` \ No newline at end of file diff --git a/src/docs/mixed_case_hex_literals.txt b/src/docs/mixed_case_hex_literals.txt deleted file mode 100644 index d2d01e0c98eb..000000000000 --- a/src/docs/mixed_case_hex_literals.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Warns on hexadecimal literals with mixed-case letter -digits. - -### Why is this bad? -It looks confusing. - -### Example -``` -0x1a9BAcD -``` - -Use instead: -``` -0x1A9BACD -``` \ No newline at end of file diff --git a/src/docs/mixed_read_write_in_expression.txt b/src/docs/mixed_read_write_in_expression.txt deleted file mode 100644 index 02d1c5d05252..000000000000 --- a/src/docs/mixed_read_write_in_expression.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for a read and a write to the same variable where -whether the read occurs before or after the write depends on the evaluation -order of sub-expressions. - -### Why is this bad? -It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands), -the operands of these expressions are evaluated before applying the effects of the expression. - -### Known problems -Code which intentionally depends on the evaluation -order, or which is correct for any evaluation order. - -### Example -``` -let mut x = 0; - -let a = { - x = 1; - 1 -} + x; -// Unclear whether a is 1 or 2. -``` - -Use instead: -``` -let tmp = { - x = 1; - 1 -}; -let a = tmp + x; -``` \ No newline at end of file diff --git a/src/docs/mod_module_files.txt b/src/docs/mod_module_files.txt deleted file mode 100644 index 95bca583afd3..000000000000 --- a/src/docs/mod_module_files.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks that module layout uses only self named module files, bans `mod.rs` files. - -### Why is this bad? -Having multiple module layout styles in a project can be confusing. - -### Example -``` -src/ - stuff/ - stuff_files.rs - mod.rs - lib.rs -``` -Use instead: -``` -src/ - stuff/ - stuff_files.rs - stuff.rs - lib.rs -``` \ No newline at end of file diff --git a/src/docs/module_inception.txt b/src/docs/module_inception.txt deleted file mode 100644 index d80a1b8d8fe5..000000000000 --- a/src/docs/module_inception.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for modules that have the same name as their -parent module - -### Why is this bad? -A typical beginner mistake is to have `mod foo;` and -again `mod foo { .. -}` in `foo.rs`. -The expectation is that items inside the inner `mod foo { .. }` are then -available -through `foo::x`, but they are only available through -`foo::foo::x`. -If this is done on purpose, it would be better to choose a more -representative module name. - -### Example -``` -// lib.rs -mod foo; -// foo.rs -mod foo { - ... -} -``` \ No newline at end of file diff --git a/src/docs/module_name_repetitions.txt b/src/docs/module_name_repetitions.txt deleted file mode 100644 index 3bc05d02780c..000000000000 --- a/src/docs/module_name_repetitions.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Detects type names that are prefixed or suffixed by the -containing module's name. - -### Why is this bad? -It requires the user to type the module name twice. - -### Example -``` -mod cake { - struct BlackForestCake; -} -``` - -Use instead: -``` -mod cake { - struct BlackForest; -} -``` \ No newline at end of file diff --git a/src/docs/modulo_arithmetic.txt b/src/docs/modulo_arithmetic.txt deleted file mode 100644 index ff7296f3c5b8..000000000000 --- a/src/docs/modulo_arithmetic.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for modulo arithmetic. - -### Why is this bad? -The results of modulo (%) operation might differ -depending on the language, when negative numbers are involved. -If you interop with different languages it might be beneficial -to double check all places that use modulo arithmetic. - -For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`. - -### Example -``` -let x = -17 % 3; -``` \ No newline at end of file diff --git a/src/docs/modulo_one.txt b/src/docs/modulo_one.txt deleted file mode 100644 index bc8f95b0be69..000000000000 --- a/src/docs/modulo_one.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for getting the remainder of a division by one or minus -one. - -### Why is this bad? -The result for a divisor of one can only ever be zero; for -minus one it can cause panic/overflow (if the left operand is the minimal value of -the respective integer type) or results in zero. No one will write such code -deliberately, unless trying to win an Underhanded Rust Contest. Even for that -contest, it's probably a bad idea. Use something more underhanded. - -### Example -``` -let a = x % 1; -let a = x % -1; -``` \ No newline at end of file diff --git a/src/docs/multi_assignments.txt b/src/docs/multi_assignments.txt deleted file mode 100644 index ed1f1b420cbd..000000000000 --- a/src/docs/multi_assignments.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for nested assignments. - -### Why is this bad? -While this is in most cases already a type mismatch, -the result of an assignment being `()` can throw off people coming from languages like python or C, -where such assignments return a copy of the assigned value. - -### Example -``` -a = b = 42; -``` -Use instead: -``` -b = 42; -a = b; -``` \ No newline at end of file diff --git a/src/docs/multiple_crate_versions.txt b/src/docs/multiple_crate_versions.txt deleted file mode 100644 index cf2d2c6abee3..000000000000 --- a/src/docs/multiple_crate_versions.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks to see if multiple versions of a crate are being -used. - -### Why is this bad? -This bloats the size of targets, and can lead to -confusing error messages when structs or traits are used interchangeably -between different versions of a crate. - -### Known problems -Because this can be caused purely by the dependencies -themselves, it's not always possible to fix this issue. - -### Example -``` -[dependencies] -ctrlc = "=3.1.0" -ansi_term = "=0.11.0" -``` \ No newline at end of file diff --git a/src/docs/multiple_inherent_impl.txt b/src/docs/multiple_inherent_impl.txt deleted file mode 100644 index 9d42286560ce..000000000000 --- a/src/docs/multiple_inherent_impl.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for multiple inherent implementations of a struct - -### Why is this bad? -Splitting the implementation of a type makes the code harder to navigate. - -### Example -``` -struct X; -impl X { - fn one() {} -} -impl X { - fn other() {} -} -``` - -Could be written: - -``` -struct X; -impl X { - fn one() {} - fn other() {} -} -``` \ No newline at end of file diff --git a/src/docs/must_use_candidate.txt b/src/docs/must_use_candidate.txt deleted file mode 100644 index 70890346fe6b..000000000000 --- a/src/docs/must_use_candidate.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for public functions that have no -`#[must_use]` attribute, but return something not already marked -must-use, have no mutable arg and mutate no statics. - -### Why is this bad? -Not bad at all, this lint just shows places where -you could add the attribute. - -### Known problems -The lint only checks the arguments for mutable -types without looking if they are actually changed. On the other hand, -it also ignores a broad range of potentially interesting side effects, -because we cannot decide whether the programmer intends the function to -be called for the side effect or the result. Expect many false -positives. At least we don't lint if the result type is unit or already -`#[must_use]`. - -### Examples -``` -// this could be annotated with `#[must_use]`. -fn id(t: T) -> T { t } -``` \ No newline at end of file diff --git a/src/docs/must_use_unit.txt b/src/docs/must_use_unit.txt deleted file mode 100644 index cabbb23f8651..000000000000 --- a/src/docs/must_use_unit.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for a `#[must_use]` attribute on -unit-returning functions and methods. - -### Why is this bad? -Unit values are useless. The attribute is likely -a remnant of a refactoring that removed the return type. - -### Examples -``` -#[must_use] -fn useless() { } -``` \ No newline at end of file diff --git a/src/docs/mut_from_ref.txt b/src/docs/mut_from_ref.txt deleted file mode 100644 index cc1da12549a5..000000000000 --- a/src/docs/mut_from_ref.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -This lint checks for functions that take immutable references and return -mutable ones. This will not trigger if no unsafe code exists as there -are multiple safe functions which will do this transformation - -To be on the conservative side, if there's at least one mutable -reference with the output lifetime, this lint will not trigger. - -### Why is this bad? -Creating a mutable reference which can be repeatably derived from an -immutable reference is unsound as it allows creating multiple live -mutable references to the same object. - -This [error](https://github.com/rust-lang/rust/issues/39465) actually -lead to an interim Rust release 1.15.1. - -### Known problems -This pattern is used by memory allocators to allow allocating multiple -objects while returning mutable references to each one. So long as -different mutable references are returned each time such a function may -be safe. - -### Example -``` -fn foo(&Foo) -> &mut Bar { .. } -``` \ No newline at end of file diff --git a/src/docs/mut_mut.txt b/src/docs/mut_mut.txt deleted file mode 100644 index 0bd34dd24b26..000000000000 --- a/src/docs/mut_mut.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for instances of `mut mut` references. - -### Why is this bad? -Multiple `mut`s don't add anything meaningful to the -source. This is either a copy'n'paste error, or it shows a fundamental -misunderstanding of references. - -### Example -``` -let x = &mut &mut y; -``` \ No newline at end of file diff --git a/src/docs/mut_mutex_lock.txt b/src/docs/mut_mutex_lock.txt deleted file mode 100644 index 5e9ad8a3f176..000000000000 --- a/src/docs/mut_mutex_lock.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for `&mut Mutex::lock` calls - -### Why is this bad? -`Mutex::lock` is less efficient than -calling `Mutex::get_mut`. In addition you also have a statically -guarantee that the mutex isn't locked, instead of just a runtime -guarantee. - -### Example -``` -use std::sync::{Arc, Mutex}; - -let mut value_rc = Arc::new(Mutex::new(42_u8)); -let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); - -let mut value = value_mutex.lock().unwrap(); -*value += 1; -``` -Use instead: -``` -use std::sync::{Arc, Mutex}; - -let mut value_rc = Arc::new(Mutex::new(42_u8)); -let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); - -let value = value_mutex.get_mut().unwrap(); -*value += 1; -``` \ No newline at end of file diff --git a/src/docs/mut_range_bound.txt b/src/docs/mut_range_bound.txt deleted file mode 100644 index e9c38a543b14..000000000000 --- a/src/docs/mut_range_bound.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for loops which have a range bound that is a mutable variable - -### Why is this bad? -One might think that modifying the mutable variable changes the loop bounds - -### Known problems -False positive when mutation is followed by a `break`, but the `break` is not immediately -after the mutation: - -``` -let mut x = 5; -for _ in 0..x { - x += 1; // x is a range bound that is mutated - ..; // some other expression - break; // leaves the loop, so mutation is not an issue -} -``` - -False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072)) - -### Example -``` -let mut foo = 42; -for i in 0..foo { - foo -= 1; - println!("{}", i); // prints numbers from 0 to 42, not 0 to 21 -} -``` \ No newline at end of file diff --git a/src/docs/mutable_key_type.txt b/src/docs/mutable_key_type.txt deleted file mode 100644 index 15fe34f2bb5e..000000000000 --- a/src/docs/mutable_key_type.txt +++ /dev/null @@ -1,61 +0,0 @@ -### What it does -Checks for sets/maps with mutable key types. - -### Why is this bad? -All of `HashMap`, `HashSet`, `BTreeMap` and -`BtreeSet` rely on either the hash or the order of keys be unchanging, -so having types with interior mutability is a bad idea. - -### Known problems - -#### False Positives -It's correct to use a struct that contains interior mutability as a key, when its -implementation of `Hash` or `Ord` doesn't access any of the interior mutable types. -However, this lint is unable to recognize this, so it will often cause false positives in -theses cases. The `bytes` crate is a great example of this. - -#### False Negatives -For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind -indirection. For example, `struct BadKey<'a>(&'a Cell)` will be seen as immutable -and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`. - -This lint does check a few cases for indirection. Firstly, using some standard library -types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and -`BTreeSet`) directly as keys (e.g. in `HashMap>, ()>`) **will** trigger the -lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their -contained type. - -Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`) -apply only to the **address** of the contained value. Therefore, interior mutability -behind raw pointers (e.g. in `HashSet<*mut Cell>`) can't impact the value of `Hash` -or `Ord`, and therefore will not trigger this link. For more info, see issue -[#6745](https://github.com/rust-lang/rust-clippy/issues/6745). - -### Example -``` -use std::cmp::{PartialEq, Eq}; -use std::collections::HashSet; -use std::hash::{Hash, Hasher}; -use std::sync::atomic::AtomicUsize; - -struct Bad(AtomicUsize); -impl PartialEq for Bad { - fn eq(&self, rhs: &Self) -> bool { - .. -; unimplemented!(); - } -} - -impl Eq for Bad {} - -impl Hash for Bad { - fn hash(&self, h: &mut H) { - .. -; unimplemented!(); - } -} - -fn main() { - let _: HashSet = HashSet::new(); -} -``` \ No newline at end of file diff --git a/src/docs/mutex_atomic.txt b/src/docs/mutex_atomic.txt deleted file mode 100644 index 062ac8b323b7..000000000000 --- a/src/docs/mutex_atomic.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usages of `Mutex` where an atomic will do. - -### Why is this bad? -Using a mutex just to make access to a plain bool or -reference sequential is shooting flies with cannons. -`std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and -faster. - -### Known problems -This lint cannot detect if the mutex is actually used -for waiting before a critical section. - -### Example -``` -let x = Mutex::new(&y); -``` - -Use instead: -``` -let x = AtomicBool::new(y); -``` \ No newline at end of file diff --git a/src/docs/mutex_integer.txt b/src/docs/mutex_integer.txt deleted file mode 100644 index f9dbdfb904c9..000000000000 --- a/src/docs/mutex_integer.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usages of `Mutex` where `X` is an integral -type. - -### Why is this bad? -Using a mutex just to make access to a plain integer -sequential is -shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster. - -### Known problems -This lint cannot detect if the mutex is actually used -for waiting before a critical section. - -### Example -``` -let x = Mutex::new(0usize); -``` - -Use instead: -``` -let x = AtomicUsize::new(0usize); -``` \ No newline at end of file diff --git a/src/docs/naive_bytecount.txt b/src/docs/naive_bytecount.txt deleted file mode 100644 index 24659dc79ae7..000000000000 --- a/src/docs/naive_bytecount.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for naive byte counts - -### Why is this bad? -The [`bytecount`](https://crates.io/crates/bytecount) -crate has methods to count your bytes faster, especially for large slices. - -### Known problems -If you have predominantly small slices, the -`bytecount::count(..)` method may actually be slower. However, if you can -ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be -faster in those cases. - -### Example -``` -let count = vec.iter().filter(|x| **x == 0u8).count(); -``` - -Use instead: -``` -let count = bytecount::count(&vec, 0u8); -``` \ No newline at end of file diff --git a/src/docs/needless_arbitrary_self_type.txt b/src/docs/needless_arbitrary_self_type.txt deleted file mode 100644 index 8216a3a3fb6e..000000000000 --- a/src/docs/needless_arbitrary_self_type.txt +++ /dev/null @@ -1,44 +0,0 @@ -### What it does -The lint checks for `self` in fn parameters that -specify the `Self`-type explicitly -### Why is this bad? -Increases the amount and decreases the readability of code - -### Example -``` -enum ValType { - I32, - I64, - F32, - F64, -} - -impl ValType { - pub fn bytes(self: Self) -> usize { - match self { - Self::I32 | Self::F32 => 4, - Self::I64 | Self::F64 => 8, - } - } -} -``` - -Could be rewritten as - -``` -enum ValType { - I32, - I64, - F32, - F64, -} - -impl ValType { - pub fn bytes(self) -> usize { - match self { - Self::I32 | Self::F32 => 4, - Self::I64 | Self::F64 => 8, - } - } -} -``` \ No newline at end of file diff --git a/src/docs/needless_bitwise_bool.txt b/src/docs/needless_bitwise_bool.txt deleted file mode 100644 index fcd7b730aaae..000000000000 --- a/src/docs/needless_bitwise_bool.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using -a lazy and. - -### Why is this bad? -The bitwise operators do not support short-circuiting, so it may hinder code performance. -Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold` - -### Known problems -This lint evaluates only when the right side is determined to have no side effects. At this time, that -determination is quite conservative. - -### Example -``` -let (x,y) = (true, false); -if x & !y {} // where both x and y are booleans -``` -Use instead: -``` -let (x,y) = (true, false); -if x && !y {} -``` \ No newline at end of file diff --git a/src/docs/needless_bool.txt b/src/docs/needless_bool.txt deleted file mode 100644 index b5c78871f14f..000000000000 --- a/src/docs/needless_bool.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for expressions of the form `if c { true } else { -false }` (or vice versa) and suggests using the condition directly. - -### Why is this bad? -Redundant code. - -### Known problems -Maybe false positives: Sometimes, the two branches are -painstakingly documented (which we, of course, do not detect), so they *may* -have some value. Even then, the documentation can be rewritten to match the -shorter code. - -### Example -``` -if x { - false -} else { - true -} -``` - -Use instead: -``` -!x -``` \ No newline at end of file diff --git a/src/docs/needless_borrow.txt b/src/docs/needless_borrow.txt deleted file mode 100644 index 4debcf473723..000000000000 --- a/src/docs/needless_borrow.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for address of operations (`&`) that are going to -be dereferenced immediately by the compiler. - -### Why is this bad? -Suggests that the receiver of the expression borrows -the expression. - -### Example -``` -fn fun(_a: &i32) {} - -let x: &i32 = &&&&&&5; -fun(&x); -``` - -Use instead: -``` -let x: &i32 = &5; -fun(x); -``` \ No newline at end of file diff --git a/src/docs/needless_borrowed_reference.txt b/src/docs/needless_borrowed_reference.txt deleted file mode 100644 index 152459ba1c9d..000000000000 --- a/src/docs/needless_borrowed_reference.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for bindings that needlessly destructure a reference and borrow the inner -value with `&ref`. - -### Why is this bad? -This pattern has no effect in almost all cases. - -### Example -``` -let mut v = Vec::::new(); -v.iter_mut().filter(|&ref a| a.is_empty()); - -if let &[ref first, ref second] = v.as_slice() {} -``` - -Use instead: -``` -let mut v = Vec::::new(); -v.iter_mut().filter(|a| a.is_empty()); - -if let [first, second] = v.as_slice() {} -``` \ No newline at end of file diff --git a/src/docs/needless_collect.txt b/src/docs/needless_collect.txt deleted file mode 100644 index 275c39afc9df..000000000000 --- a/src/docs/needless_collect.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for functions collecting an iterator when collect -is not needed. - -### Why is this bad? -`collect` causes the allocation of a new data structure, -when this allocation may not be needed. - -### Example -``` -let len = iterator.clone().collect::>().len(); -// should be -let len = iterator.count(); -``` \ No newline at end of file diff --git a/src/docs/needless_continue.txt b/src/docs/needless_continue.txt deleted file mode 100644 index 2cee621c1af0..000000000000 --- a/src/docs/needless_continue.txt +++ /dev/null @@ -1,61 +0,0 @@ -### What it does -The lint checks for `if`-statements appearing in loops -that contain a `continue` statement in either their main blocks or their -`else`-blocks, when omitting the `else`-block possibly with some -rearrangement of code can make the code easier to understand. - -### Why is this bad? -Having explicit `else` blocks for `if` statements -containing `continue` in their THEN branch adds unnecessary branching and -nesting to the code. Having an else block containing just `continue` can -also be better written by grouping the statements following the whole `if` -statement within the THEN block and omitting the else block completely. - -### Example -``` -while condition() { - update_condition(); - if x { - // ... - } else { - continue; - } - println!("Hello, world"); -} -``` - -Could be rewritten as - -``` -while condition() { - update_condition(); - if x { - // ... - println!("Hello, world"); - } -} -``` - -As another example, the following code - -``` -loop { - if waiting() { - continue; - } else { - // Do something useful - } - # break; -} -``` -Could be rewritten as - -``` -loop { - if waiting() { - continue; - } - // Do something useful - # break; -} -``` \ No newline at end of file diff --git a/src/docs/needless_doctest_main.txt b/src/docs/needless_doctest_main.txt deleted file mode 100644 index 8f91a7baa715..000000000000 --- a/src/docs/needless_doctest_main.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for `fn main() { .. }` in doctests - -### Why is this bad? -The test can be shorter (and likely more readable) -if the `fn main()` is left implicit. - -### Examples -``` -/// An example of a doctest with a `main()` function -/// -/// # Examples -/// -/// ``` -/// fn main() { -/// // this needs not be in an `fn` -/// } -/// ``` -fn needless_main() { - unimplemented!(); -} -``` \ No newline at end of file diff --git a/src/docs/needless_for_each.txt b/src/docs/needless_for_each.txt deleted file mode 100644 index 9ae6dd360c8f..000000000000 --- a/src/docs/needless_for_each.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for usage of `for_each` that would be more simply written as a -`for` loop. - -### Why is this bad? -`for_each` may be used after applying iterator transformers like -`filter` for better readability and performance. It may also be used to fit a simple -operation on one line. -But when none of these apply, a simple `for` loop is more idiomatic. - -### Example -``` -let v = vec![0, 1, 2]; -v.iter().for_each(|elem| { - println!("{}", elem); -}) -``` -Use instead: -``` -let v = vec![0, 1, 2]; -for elem in v.iter() { - println!("{}", elem); -} -``` \ No newline at end of file diff --git a/src/docs/needless_late_init.txt b/src/docs/needless_late_init.txt deleted file mode 100644 index 9e7bbcea9982..000000000000 --- a/src/docs/needless_late_init.txt +++ /dev/null @@ -1,42 +0,0 @@ -### What it does -Checks for late initializations that can be replaced by a `let` statement -with an initializer. - -### Why is this bad? -Assigning in the `let` statement is less repetitive. - -### Example -``` -let a; -a = 1; - -let b; -match 3 { - 0 => b = "zero", - 1 => b = "one", - _ => b = "many", -} - -let c; -if true { - c = 1; -} else { - c = -1; -} -``` -Use instead: -``` -let a = 1; - -let b = match 3 { - 0 => "zero", - 1 => "one", - _ => "many", -}; - -let c = if true { - 1 -} else { - -1 -}; -``` \ No newline at end of file diff --git a/src/docs/needless_lifetimes.txt b/src/docs/needless_lifetimes.txt deleted file mode 100644 index b280caa66b58..000000000000 --- a/src/docs/needless_lifetimes.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for lifetime annotations which can be removed by -relying on lifetime elision. - -### Why is this bad? -The additional lifetimes make the code look more -complicated, while there is nothing out of the ordinary going on. Removing -them leads to more readable code. - -### Known problems -- We bail out if the function has a `where` clause where lifetimes -are mentioned due to potential false positives. -- Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the -placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. - -### Example -``` -// Unnecessary lifetime annotations -fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { - x -} -``` - -Use instead: -``` -fn elided(x: &u8, y: u8) -> &u8 { - x -} -``` \ No newline at end of file diff --git a/src/docs/needless_match.txt b/src/docs/needless_match.txt deleted file mode 100644 index 92b40a5df648..000000000000 --- a/src/docs/needless_match.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result` -when function signatures are the same. - -### Why is this bad? -This `match` block does nothing and might not be what the coder intended. - -### Example -``` -fn foo() -> Result<(), i32> { - match result { - Ok(val) => Ok(val), - Err(err) => Err(err), - } -} - -fn bar() -> Option { - if let Some(val) = option { - Some(val) - } else { - None - } -} -``` - -Could be replaced as - -``` -fn foo() -> Result<(), i32> { - result -} - -fn bar() -> Option { - option -} -``` \ No newline at end of file diff --git a/src/docs/needless_option_as_deref.txt b/src/docs/needless_option_as_deref.txt deleted file mode 100644 index 226396c97ac4..000000000000 --- a/src/docs/needless_option_as_deref.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for no-op uses of `Option::{as_deref, as_deref_mut}`, -for example, `Option<&T>::as_deref()` returns the same type. - -### Why is this bad? -Redundant code and improving readability. - -### Example -``` -let a = Some(&1); -let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> -``` - -Use instead: -``` -let a = Some(&1); -let b = a; -``` \ No newline at end of file diff --git a/src/docs/needless_option_take.txt b/src/docs/needless_option_take.txt deleted file mode 100644 index 6bac65a13b5b..000000000000 --- a/src/docs/needless_option_take.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for calling `take` function after `as_ref`. - -### Why is this bad? -Redundant code. `take` writes `None` to its argument. -In this case the modification is useless as it's a temporary that cannot be read from afterwards. - -### Example -``` -let x = Some(3); -x.as_ref().take(); -``` -Use instead: -``` -let x = Some(3); -x.as_ref(); -``` \ No newline at end of file diff --git a/src/docs/needless_parens_on_range_literals.txt b/src/docs/needless_parens_on_range_literals.txt deleted file mode 100644 index 85fab10cb5f6..000000000000 --- a/src/docs/needless_parens_on_range_literals.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -The lint checks for parenthesis on literals in range statements that are -superfluous. - -### Why is this bad? -Having superfluous parenthesis makes the code less readable -overhead when reading. - -### Example - -``` -for i in (0)..10 { - println!("{i}"); -} -``` - -Use instead: - -``` -for i in 0..10 { - println!("{i}"); -} -``` \ No newline at end of file diff --git a/src/docs/needless_pass_by_value.txt b/src/docs/needless_pass_by_value.txt deleted file mode 100644 index 58c420b19f62..000000000000 --- a/src/docs/needless_pass_by_value.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for functions taking arguments by value, but not -consuming them in its -body. - -### Why is this bad? -Taking arguments by reference is more flexible and can -sometimes avoid -unnecessary allocations. - -### Known problems -* This lint suggests taking an argument by reference, -however sometimes it is better to let users decide the argument type -(by using `Borrow` trait, for example), depending on how the function is used. - -### Example -``` -fn foo(v: Vec) { - assert_eq!(v.len(), 42); -} -``` -should be -``` -fn foo(v: &[i32]) { - assert_eq!(v.len(), 42); -} -``` \ No newline at end of file diff --git a/src/docs/needless_question_mark.txt b/src/docs/needless_question_mark.txt deleted file mode 100644 index 540739fd45fa..000000000000 --- a/src/docs/needless_question_mark.txt +++ /dev/null @@ -1,43 +0,0 @@ -### What it does -Suggests alternatives for useless applications of `?` in terminating expressions - -### Why is this bad? -There's no reason to use `?` to short-circuit when execution of the body will end there anyway. - -### Example -``` -struct TO { - magic: Option, -} - -fn f(to: TO) -> Option { - Some(to.magic?) -} - -struct TR { - magic: Result, -} - -fn g(tr: Result) -> Result { - tr.and_then(|t| Ok(t.magic?)) -} - -``` -Use instead: -``` -struct TO { - magic: Option, -} - -fn f(to: TO) -> Option { - to.magic -} - -struct TR { - magic: Result, -} - -fn g(tr: Result) -> Result { - tr.and_then(|t| t.magic) -} -``` \ No newline at end of file diff --git a/src/docs/needless_range_loop.txt b/src/docs/needless_range_loop.txt deleted file mode 100644 index 583c09b28497..000000000000 --- a/src/docs/needless_range_loop.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for looping over the range of `0..len` of some -collection just to get the values by index. - -### Why is this bad? -Just iterating the collection itself makes the intent -more clear and is probably faster. - -### Example -``` -let vec = vec!['a', 'b', 'c']; -for i in 0..vec.len() { - println!("{}", vec[i]); -} -``` - -Use instead: -``` -let vec = vec!['a', 'b', 'c']; -for i in vec { - println!("{}", i); -} -``` \ No newline at end of file diff --git a/src/docs/needless_return.txt b/src/docs/needless_return.txt deleted file mode 100644 index 48782cb0ca84..000000000000 --- a/src/docs/needless_return.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for return statements at the end of a block. - -### Why is this bad? -Removing the `return` and semicolon will make the code -more rusty. - -### Example -``` -fn foo(x: usize) -> usize { - return x; -} -``` -simplify to -``` -fn foo(x: usize) -> usize { - x -} -``` \ No newline at end of file diff --git a/src/docs/needless_splitn.txt b/src/docs/needless_splitn.txt deleted file mode 100644 index b10a84fbc42d..000000000000 --- a/src/docs/needless_splitn.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same. -### Why is this bad? -The function `split` is simpler and there is no performance difference in these cases, considering -that both functions return a lazy iterator. -### Example -``` -let str = "key=value=add"; -let _ = str.splitn(3, '=').next().unwrap(); -``` - -Use instead: -``` -let str = "key=value=add"; -let _ = str.split('=').next().unwrap(); -``` \ No newline at end of file diff --git a/src/docs/needless_update.txt b/src/docs/needless_update.txt deleted file mode 100644 index 82adabf6482c..000000000000 --- a/src/docs/needless_update.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for needlessly including a base struct on update -when all fields are changed anyway. - -This lint is not applied to structs marked with -[non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html). - -### Why is this bad? -This will cost resources (because the base has to be -somewhere), and make the code less readable. - -### Example -``` -Point { - x: 1, - y: 1, - z: 1, - ..zero_point -}; -``` - -Use instead: -``` -// Missing field `z` -Point { - x: 1, - y: 1, - ..zero_point -}; -``` \ No newline at end of file diff --git a/src/docs/neg_cmp_op_on_partial_ord.txt b/src/docs/neg_cmp_op_on_partial_ord.txt deleted file mode 100644 index fa55c6cfd74b..000000000000 --- a/src/docs/neg_cmp_op_on_partial_ord.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for the usage of negated comparison operators on types which only implement -`PartialOrd` (e.g., `f64`). - -### Why is this bad? -These operators make it easy to forget that the underlying types actually allow not only three -potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is -especially easy to miss if the operator based comparison result is negated. - -### Example -``` -let a = 1.0; -let b = f64::NAN; - -let not_less_or_equal = !(a <= b); -``` - -Use instead: -``` -use std::cmp::Ordering; - -let _not_less_or_equal = match a.partial_cmp(&b) { - None | Some(Ordering::Greater) => true, - _ => false, -}; -``` \ No newline at end of file diff --git a/src/docs/neg_multiply.txt b/src/docs/neg_multiply.txt deleted file mode 100644 index 4e8b096eb9cc..000000000000 --- a/src/docs/neg_multiply.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for multiplication by -1 as a form of negation. - -### Why is this bad? -It's more readable to just negate. - -### Known problems -This only catches integers (for now). - -### Example -``` -let a = x * -1; -``` - -Use instead: -``` -let a = -x; -``` \ No newline at end of file diff --git a/src/docs/negative_feature_names.txt b/src/docs/negative_feature_names.txt deleted file mode 100644 index 01ee9efb318b..000000000000 --- a/src/docs/negative_feature_names.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for negative feature names with prefix `no-` or `not-` - -### Why is this bad? -Features are supposed to be additive, and negatively-named features violate it. - -### Example -``` -[features] -default = [] -no-abc = [] -not-def = [] - -``` -Use instead: -``` -[features] -default = ["abc", "def"] -abc = [] -def = [] - -``` \ No newline at end of file diff --git a/src/docs/never_loop.txt b/src/docs/never_loop.txt deleted file mode 100644 index 737ccf415cb9..000000000000 --- a/src/docs/never_loop.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for loops that will always `break`, `return` or -`continue` an outer loop. - -### Why is this bad? -This loop never loops, all it does is obfuscating the -code. - -### Example -``` -loop { - ..; - break; -} -``` \ No newline at end of file diff --git a/src/docs/new_ret_no_self.txt b/src/docs/new_ret_no_self.txt deleted file mode 100644 index 291bad24a643..000000000000 --- a/src/docs/new_ret_no_self.txt +++ /dev/null @@ -1,47 +0,0 @@ -### What it does -Checks for `new` not returning a type that contains `Self`. - -### Why is this bad? -As a convention, `new` methods are used to make a new -instance of a type. - -### Example -In an impl block: -``` -impl Foo { - fn new() -> NotAFoo { - } -} -``` - -``` -struct Bar(Foo); -impl Foo { - // Bad. The type name must contain `Self` - fn new() -> Bar { - } -} -``` - -``` -impl Foo { - // Good. Return type contains `Self` - fn new() -> Result { - } -} -``` - -Or in a trait definition: -``` -pub trait Trait { - // Bad. The type name must contain `Self` - fn new(); -} -``` - -``` -pub trait Trait { - // Good. Return type contains `Self` - fn new() -> Self; -} -``` \ No newline at end of file diff --git a/src/docs/new_without_default.txt b/src/docs/new_without_default.txt deleted file mode 100644 index 662d39c8efdd..000000000000 --- a/src/docs/new_without_default.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for public types with a `pub fn new() -> Self` method and no -implementation of -[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html). - -### Why is this bad? -The user might expect to be able to use -[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the -type can be constructed without arguments. - -### Example -``` -pub struct Foo(Bar); - -impl Foo { - pub fn new() -> Self { - Foo(Bar::new()) - } -} -``` - -To fix the lint, add a `Default` implementation that delegates to `new`: - -``` -pub struct Foo(Bar); - -impl Default for Foo { - fn default() -> Self { - Foo::new() - } -} -``` \ No newline at end of file diff --git a/src/docs/no_effect.txt b/src/docs/no_effect.txt deleted file mode 100644 index d4cc08fa8a7d..000000000000 --- a/src/docs/no_effect.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for statements which have no effect. - -### Why is this bad? -Unlike dead code, these statements are actually -executed. However, as they have no effect, all they do is make the code less -readable. - -### Example -``` -0; -``` \ No newline at end of file diff --git a/src/docs/no_effect_replace.txt b/src/docs/no_effect_replace.txt deleted file mode 100644 index 646d45287ef5..000000000000 --- a/src/docs/no_effect_replace.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for `replace` statements which have no effect. - -### Why is this bad? -It's either a mistake or confusing. - -### Example -``` -"1234".replace("12", "12"); -"1234".replacen("12", "12", 1); -``` \ No newline at end of file diff --git a/src/docs/no_effect_underscore_binding.txt b/src/docs/no_effect_underscore_binding.txt deleted file mode 100644 index 972f60dd01e9..000000000000 --- a/src/docs/no_effect_underscore_binding.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for binding to underscore prefixed variable without side-effects. - -### Why is this bad? -Unlike dead code, these bindings are actually -executed. However, as they have no effect and shouldn't be used further on, all they -do is make the code less readable. - -### Known problems -Further usage of this variable is not checked, which can lead to false positives if it is -used later in the code. - -### Example -``` -let _i_serve_no_purpose = 1; -``` \ No newline at end of file diff --git a/src/docs/non_ascii_literal.txt b/src/docs/non_ascii_literal.txt deleted file mode 100644 index 164902b4726e..000000000000 --- a/src/docs/non_ascii_literal.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for non-ASCII characters in string and char literals. - -### Why is this bad? -Yeah, we know, the 90's called and wanted their charset -back. Even so, there still are editors and other programs out there that -don't work well with Unicode. So if the code is meant to be used -internationally, on multiple operating systems, or has other portability -requirements, activating this lint could be useful. - -### Example -``` -let x = String::from("€"); -``` - -Use instead: -``` -let x = String::from("\u{20ac}"); -``` \ No newline at end of file diff --git a/src/docs/non_octal_unix_permissions.txt b/src/docs/non_octal_unix_permissions.txt deleted file mode 100644 index 4a468e94db16..000000000000 --- a/src/docs/non_octal_unix_permissions.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for non-octal values used to set Unix file permissions. - -### Why is this bad? -They will be converted into octal, creating potentially -unintended file permissions. - -### Example -``` -use std::fs::OpenOptions; -use std::os::unix::fs::OpenOptionsExt; - -let mut options = OpenOptions::new(); -options.mode(644); -``` -Use instead: -``` -use std::fs::OpenOptions; -use std::os::unix::fs::OpenOptionsExt; - -let mut options = OpenOptions::new(); -options.mode(0o644); -``` \ No newline at end of file diff --git a/src/docs/non_send_fields_in_send_ty.txt b/src/docs/non_send_fields_in_send_ty.txt deleted file mode 100644 index 11e6f6e162cf..000000000000 --- a/src/docs/non_send_fields_in_send_ty.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -This lint warns about a `Send` implementation for a type that -contains fields that are not safe to be sent across threads. -It tries to detect fields that can cause a soundness issue -when sent to another thread (e.g., `Rc`) while allowing `!Send` fields -that are expected to exist in a `Send` type, such as raw pointers. - -### Why is this bad? -Sending the struct to another thread effectively sends all of its fields, -and the fields that do not implement `Send` can lead to soundness bugs -such as data races when accessed in a thread -that is different from the thread that created it. - -See: -* [*The Rustonomicon* about *Send and Sync*](https://doc.rust-lang.org/nomicon/send-and-sync.html) -* [The documentation of `Send`](https://doc.rust-lang.org/std/marker/trait.Send.html) - -### Known Problems -This lint relies on heuristics to distinguish types that are actually -unsafe to be sent across threads and `!Send` types that are expected to -exist in `Send` type. Its rule can filter out basic cases such as -`Vec<*const T>`, but it's not perfect. Feel free to create an issue if -you have a suggestion on how this heuristic can be improved. - -### Example -``` -struct ExampleStruct { - rc_is_not_send: Rc, - unbounded_generic_field: T, -} - -// This impl is unsound because it allows sending `!Send` types through `ExampleStruct` -unsafe impl Send for ExampleStruct {} -``` -Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) -or specify correct bounds on generic type parameters (`T: Send`). \ No newline at end of file diff --git a/src/docs/nonminimal_bool.txt b/src/docs/nonminimal_bool.txt deleted file mode 100644 index 488980ddf023..000000000000 --- a/src/docs/nonminimal_bool.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for boolean expressions that can be written more -concisely. - -### Why is this bad? -Readability of boolean expressions suffers from -unnecessary duplication. - -### Known problems -Ignores short circuiting behavior of `||` and -`&&`. Ignores `|`, `&` and `^`. - -### Example -``` -if a && true {} -if !(a == b) {} -``` - -Use instead: -``` -if a {} -if a != b {} -``` \ No newline at end of file diff --git a/src/docs/nonsensical_open_options.txt b/src/docs/nonsensical_open_options.txt deleted file mode 100644 index 7a95443b51a5..000000000000 --- a/src/docs/nonsensical_open_options.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for duplicate open options as well as combinations -that make no sense. - -### Why is this bad? -In the best case, the code will be harder to read than -necessary. I don't know the worst case. - -### Example -``` -use std::fs::OpenOptions; - -OpenOptions::new().read(true).truncate(true); -``` \ No newline at end of file diff --git a/src/docs/nonstandard_macro_braces.txt b/src/docs/nonstandard_macro_braces.txt deleted file mode 100644 index 7e8d0d2d33bf..000000000000 --- a/src/docs/nonstandard_macro_braces.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks that common macros are used with consistent bracing. - -### Why is this bad? -This is mostly a consistency lint although using () or [] -doesn't give you a semicolon in item position, which can be unexpected. - -### Example -``` -vec!{1, 2, 3}; -``` -Use instead: -``` -vec![1, 2, 3]; -``` \ No newline at end of file diff --git a/src/docs/not_unsafe_ptr_arg_deref.txt b/src/docs/not_unsafe_ptr_arg_deref.txt deleted file mode 100644 index 31355fbb7b66..000000000000 --- a/src/docs/not_unsafe_ptr_arg_deref.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for public functions that dereference raw pointer -arguments but are not marked `unsafe`. - -### Why is this bad? -The function should probably be marked `unsafe`, since -for an arbitrary raw pointer, there is no way of telling for sure if it is -valid. - -### Known problems -* It does not check functions recursively so if the pointer is passed to a -private non-`unsafe` function which does the dereferencing, the lint won't -trigger. -* It only checks for arguments whose type are raw pointers, not raw pointers -got from an argument in some other way (`fn foo(bar: &[*const u8])` or -`some_argument.get_raw_ptr()`). - -### Example -``` -pub fn foo(x: *const u8) { - println!("{}", unsafe { *x }); -} -``` - -Use instead: -``` -pub unsafe fn foo(x: *const u8) { - println!("{}", unsafe { *x }); -} -``` \ No newline at end of file diff --git a/src/docs/obfuscated_if_else.txt b/src/docs/obfuscated_if_else.txt deleted file mode 100644 index 638f63b0db5e..000000000000 --- a/src/docs/obfuscated_if_else.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for usages of `.then_some(..).unwrap_or(..)` - -### Why is this bad? -This can be written more clearly with `if .. else ..` - -### Limitations -This lint currently only looks for usages of -`.then_some(..).unwrap_or(..)`, but will be expanded -to account for similar patterns. - -### Example -``` -let x = true; -x.then_some("a").unwrap_or("b"); -``` -Use instead: -``` -let x = true; -if x { "a" } else { "b" }; -``` \ No newline at end of file diff --git a/src/docs/octal_escapes.txt b/src/docs/octal_escapes.txt deleted file mode 100644 index eee820587158..000000000000 --- a/src/docs/octal_escapes.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks for `\0` escapes in string and byte literals that look like octal -character escapes in C. - -### Why is this bad? - -C and other languages support octal character escapes in strings, where -a backslash is followed by up to three octal digits. For example, `\033` -stands for the ASCII character 27 (ESC). Rust does not support this -notation, but has the escape code `\0` which stands for a null -byte/character, and any following digits do not form part of the escape -sequence. Therefore, `\033` is not a compiler error but the result may -be surprising. - -### Known problems -The actual meaning can be the intended one. `\x00` can be used in these -cases to be unambiguous. - -The lint does not trigger for format strings in `print!()`, `write!()` -and friends since the string is already preprocessed when Clippy lints -can see it. - -### Example -``` -let one = "\033[1m Bold? \033[0m"; // \033 intended as escape -let two = "\033\0"; // \033 intended as null-3-3 -``` - -Use instead: -``` -let one = "\x1b[1mWill this be bold?\x1b[0m"; -let two = "\x0033\x00"; -``` \ No newline at end of file diff --git a/src/docs/ok_expect.txt b/src/docs/ok_expect.txt deleted file mode 100644 index fd5205d49dc0..000000000000 --- a/src/docs/ok_expect.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `ok().expect(..)`. - -### Why is this bad? -Because you usually call `expect()` on the `Result` -directly to get a better error message. - -### Known problems -The error type needs to implement `Debug` - -### Example -``` -x.ok().expect("why did I do this again?"); -``` - -Use instead: -``` -x.expect("why did I do this again?"); -``` \ No newline at end of file diff --git a/src/docs/only_used_in_recursion.txt b/src/docs/only_used_in_recursion.txt deleted file mode 100644 index f19f47ff9eb5..000000000000 --- a/src/docs/only_used_in_recursion.txt +++ /dev/null @@ -1,58 +0,0 @@ -### What it does -Checks for arguments that are only used in recursion with no side-effects. - -### Why is this bad? -It could contain a useless calculation and can make function simpler. - -The arguments can be involved in calculations and assignments but as long as -the calculations have no side-effects (function calls or mutating dereference) -and the assigned variables are also only in recursion, it is useless. - -### Known problems -Too many code paths in the linting code are currently untested and prone to produce false -positives or are prone to have performance implications. - -In some cases, this would not catch all useless arguments. - -``` -fn foo(a: usize, b: usize) -> usize { - let f = |x| x + 1; - - if a == 0 { - 1 - } else { - foo(a - 1, f(b)) - } -} -``` - -For example, the argument `b` is only used in recursion, but the lint would not catch it. - -List of some examples that can not be caught: -- binary operation of non-primitive types -- closure usage -- some `break` relative operations -- struct pattern binding - -Also, when you recurse the function name with path segments, it is not possible to detect. - -### Example -``` -fn f(a: usize, b: usize) -> usize { - if a == 0 { - 1 - } else { - f(a - 1, b + 1) - } -} -``` -Use instead: -``` -fn f(a: usize) -> usize { - if a == 0 { - 1 - } else { - f(a - 1) - } -} -``` \ No newline at end of file diff --git a/src/docs/op_ref.txt b/src/docs/op_ref.txt deleted file mode 100644 index 7a7ed1bc9bad..000000000000 --- a/src/docs/op_ref.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for arguments to `==` which have their address -taken to satisfy a bound -and suggests to dereference the other argument instead - -### Why is this bad? -It is more idiomatic to dereference the other argument. - -### Example -``` -&x == y -``` - -Use instead: -``` -x == *y -``` \ No newline at end of file diff --git a/src/docs/option_as_ref_deref.txt b/src/docs/option_as_ref_deref.txt deleted file mode 100644 index ad7411d3d4bd..000000000000 --- a/src/docs/option_as_ref_deref.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). - -### Why is this bad? -Readability, this can be written more concisely as -`_.as_deref()`. - -### Example -``` -opt.as_ref().map(String::as_str) -``` -Can be written as -``` -opt.as_deref() -``` \ No newline at end of file diff --git a/src/docs/option_env_unwrap.txt b/src/docs/option_env_unwrap.txt deleted file mode 100644 index c952cba8e261..000000000000 --- a/src/docs/option_env_unwrap.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `option_env!(...).unwrap()` and -suggests usage of the `env!` macro. - -### Why is this bad? -Unwrapping the result of `option_env!` will panic -at run-time if the environment variable doesn't exist, whereas `env!` -catches it at compile-time. - -### Example -``` -let _ = option_env!("HOME").unwrap(); -``` - -Is better expressed as: - -``` -let _ = env!("HOME"); -``` \ No newline at end of file diff --git a/src/docs/option_filter_map.txt b/src/docs/option_filter_map.txt deleted file mode 100644 index 25f7bde7b4d0..000000000000 --- a/src/docs/option_filter_map.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for indirect collection of populated `Option` - -### Why is this bad? -`Option` is like a collection of 0-1 things, so `flatten` -automatically does this without suspicious-looking `unwrap` calls. - -### Example -``` -let _ = std::iter::empty::>().filter(Option::is_some).map(Option::unwrap); -``` -Use instead: -``` -let _ = std::iter::empty::>().flatten(); -``` \ No newline at end of file diff --git a/src/docs/option_if_let_else.txt b/src/docs/option_if_let_else.txt deleted file mode 100644 index 43652db513b4..000000000000 --- a/src/docs/option_if_let_else.txt +++ /dev/null @@ -1,46 +0,0 @@ -### What it does -Lints usage of `if let Some(v) = ... { y } else { x }` and -`match .. { Some(v) => y, None/_ => x }` which are more -idiomatically done with `Option::map_or` (if the else bit is a pure -expression) or `Option::map_or_else` (if the else bit is an impure -expression). - -### Why is this bad? -Using the dedicated functions of the `Option` type is clearer and -more concise than an `if let` expression. - -### Known problems -This lint uses a deliberately conservative metric for checking -if the inside of either body contains breaks or continues which will -cause it to not suggest a fix if either block contains a loop with -continues or breaks contained within the loop. - -### Example -``` -let _ = if let Some(foo) = optional { - foo -} else { - 5 -}; -let _ = match optional { - Some(val) => val + 1, - None => 5 -}; -let _ = if let Some(foo) = optional { - foo -} else { - let y = do_complicated_function(); - y*y -}; -``` - -should be - -``` -let _ = optional.map_or(5, |foo| foo); -let _ = optional.map_or(5, |val| val + 1); -let _ = optional.map_or_else(||{ - let y = do_complicated_function(); - y*y -}, |foo| foo); -``` \ No newline at end of file diff --git a/src/docs/option_map_or_none.txt b/src/docs/option_map_or_none.txt deleted file mode 100644 index c86c65215f01..000000000000 --- a/src/docs/option_map_or_none.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `_.map_or(None, _)`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.and_then(_)`. - -### Known problems -The order of the arguments is not in execution order. - -### Example -``` -opt.map_or(None, |a| Some(a + 1)); -``` - -Use instead: -``` -opt.and_then(|a| Some(a + 1)); -``` \ No newline at end of file diff --git a/src/docs/option_map_unit_fn.txt b/src/docs/option_map_unit_fn.txt deleted file mode 100644 index fc4b528f0923..000000000000 --- a/src/docs/option_map_unit_fn.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for usage of `option.map(f)` where f is a function -or closure that returns the unit type `()`. - -### Why is this bad? -Readability, this can be written more clearly with -an if let statement - -### Example -``` -let x: Option = do_stuff(); -x.map(log_err_msg); -x.map(|msg| log_err_msg(format_msg(msg))); -``` - -The correct use would be: - -``` -let x: Option = do_stuff(); -if let Some(msg) = x { - log_err_msg(msg); -} - -if let Some(msg) = x { - log_err_msg(format_msg(msg)); -} -``` \ No newline at end of file diff --git a/src/docs/option_option.txt b/src/docs/option_option.txt deleted file mode 100644 index b4324bd83990..000000000000 --- a/src/docs/option_option.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for use of `Option>` in function signatures and type -definitions - -### Why is this bad? -`Option<_>` represents an optional value. `Option>` -represents an optional optional value which is logically the same thing as an optional -value but has an unneeded extra level of wrapping. - -If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases, -consider a custom `enum` instead, with clear names for each case. - -### Example -``` -fn get_data() -> Option> { - None -} -``` - -Better: - -``` -pub enum Contents { - Data(Vec), // Was Some(Some(Vec)) - NotYetFetched, // Was Some(None) - None, // Was None -} - -fn get_data() -> Contents { - Contents::None -} -``` \ No newline at end of file diff --git a/src/docs/or_fun_call.txt b/src/docs/or_fun_call.txt deleted file mode 100644 index 6ce77cc268c5..000000000000 --- a/src/docs/or_fun_call.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, -`.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`, -`.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()` -etc. instead. - -### Why is this bad? -The function will always be called and potentially -allocate an object acting as the default. - -### Known problems -If the function has side-effects, not calling it will -change the semantic of the program, but you shouldn't rely on that anyway. - -### Example -``` -foo.unwrap_or(String::new()); -``` - -Use instead: -``` -foo.unwrap_or_else(String::new); - -// or - -foo.unwrap_or_default(); -``` \ No newline at end of file diff --git a/src/docs/or_then_unwrap.txt b/src/docs/or_then_unwrap.txt deleted file mode 100644 index 64ac53749e8a..000000000000 --- a/src/docs/or_then_unwrap.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for `.or(…).unwrap()` calls to Options and Results. - -### Why is this bad? -You should use `.unwrap_or(…)` instead for clarity. - -### Example -``` -// Result -let value = result.or::(Ok(fallback)).unwrap(); - -// Option -let value = option.or(Some(fallback)).unwrap(); -``` -Use instead: -``` -// Result -let value = result.unwrap_or(fallback); - -// Option -let value = option.unwrap_or(fallback); -``` \ No newline at end of file diff --git a/src/docs/out_of_bounds_indexing.txt b/src/docs/out_of_bounds_indexing.txt deleted file mode 100644 index 5802eea29966..000000000000 --- a/src/docs/out_of_bounds_indexing.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for out of bounds array indexing with a constant -index. - -### Why is this bad? -This will always panic at runtime. - -### Example -``` -let x = [1, 2, 3, 4]; - -x[9]; -&x[2..9]; -``` - -Use instead: -``` -// Index within bounds - -x[0]; -x[3]; -``` \ No newline at end of file diff --git a/src/docs/overflow_check_conditional.txt b/src/docs/overflow_check_conditional.txt deleted file mode 100644 index a09cc18a0bcc..000000000000 --- a/src/docs/overflow_check_conditional.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Detects classic underflow/overflow checks. - -### Why is this bad? -Most classic C underflow/overflow checks will fail in -Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead. - -### Example -``` -a + b < a; -``` \ No newline at end of file diff --git a/src/docs/overly_complex_bool_expr.txt b/src/docs/overly_complex_bool_expr.txt deleted file mode 100644 index 65ca18392e75..000000000000 --- a/src/docs/overly_complex_bool_expr.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for boolean expressions that contain terminals that -can be eliminated. - -### Why is this bad? -This is most likely a logic bug. - -### Known problems -Ignores short circuiting behavior. - -### Example -``` -// The `b` is unnecessary, the expression is equivalent to `if a`. -if a && b || a { ... } -``` - -Use instead: -``` -if a {} -``` \ No newline at end of file diff --git a/src/docs/panic.txt b/src/docs/panic.txt deleted file mode 100644 index f9bdc6e87ccf..000000000000 --- a/src/docs/panic.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for usage of `panic!`. - -### Why is this bad? -`panic!` will stop the execution of the executable - -### Example -``` -panic!("even with a good reason"); -``` \ No newline at end of file diff --git a/src/docs/panic_in_result_fn.txt b/src/docs/panic_in_result_fn.txt deleted file mode 100644 index 51c2f8ae5a30..000000000000 --- a/src/docs/panic_in_result_fn.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result. - -### Why is this bad? -For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided. - -### Known problems -Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked. - -### Example -``` -fn result_with_panic() -> Result -{ - panic!("error"); -} -``` -Use instead: -``` -fn result_without_panic() -> Result { - Err(String::from("error")) -} -``` \ No newline at end of file diff --git a/src/docs/panicking_unwrap.txt b/src/docs/panicking_unwrap.txt deleted file mode 100644 index 1fbc245c8ec3..000000000000 --- a/src/docs/panicking_unwrap.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for calls of `unwrap[_err]()` that will always fail. - -### Why is this bad? -If panicking is desired, an explicit `panic!()` should be used. - -### Known problems -This lint only checks `if` conditions not assignments. -So something like `let x: Option<()> = None; x.unwrap();` will not be recognized. - -### Example -``` -if option.is_none() { - do_something_with(option.unwrap()) -} -``` - -This code will always panic. The if condition should probably be inverted. \ No newline at end of file diff --git a/src/docs/partial_pub_fields.txt b/src/docs/partial_pub_fields.txt deleted file mode 100644 index b529adf1547d..000000000000 --- a/src/docs/partial_pub_fields.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks whether partial fields of a struct are public. - -Either make all fields of a type public, or make none of them public - -### Why is this bad? -Most types should either be: -* Abstract data types: complex objects with opaque implementation which guard -interior invariants and expose intentionally limited API to the outside world. -* Data: relatively simple objects which group a bunch of related attributes together. - -### Example -``` -pub struct Color { - pub r: u8, - pub g: u8, - b: u8, -} -``` -Use instead: -``` -pub struct Color { - pub r: u8, - pub g: u8, - pub b: u8, -} -``` \ No newline at end of file diff --git a/src/docs/partialeq_ne_impl.txt b/src/docs/partialeq_ne_impl.txt deleted file mode 100644 index 78f55188bab8..000000000000 --- a/src/docs/partialeq_ne_impl.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for manual re-implementations of `PartialEq::ne`. - -### Why is this bad? -`PartialEq::ne` is required to always return the -negated result of `PartialEq::eq`, which is exactly what the default -implementation does. Therefore, there should never be any need to -re-implement it. - -### Example -``` -struct Foo; - -impl PartialEq for Foo { - fn eq(&self, other: &Foo) -> bool { true } - fn ne(&self, other: &Foo) -> bool { !(self == other) } -} -``` \ No newline at end of file diff --git a/src/docs/partialeq_to_none.txt b/src/docs/partialeq_to_none.txt deleted file mode 100644 index 5cc07bf88435..000000000000 --- a/src/docs/partialeq_to_none.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does - -Checks for binary comparisons to a literal `Option::None`. - -### Why is this bad? - -A programmer checking if some `foo` is `None` via a comparison `foo == None` -is usually inspired from other programming languages (e.g. `foo is None` -in Python). -Checking if a value of type `Option` is (not) equal to `None` in that -way relies on `T: PartialEq` to do the comparison, which is unneeded. - -### Example -``` -fn foo(f: Option) -> &'static str { - if f != None { "yay" } else { "nay" } -} -``` -Use instead: -``` -fn foo(f: Option) -> &'static str { - if f.is_some() { "yay" } else { "nay" } -} -``` \ No newline at end of file diff --git a/src/docs/path_buf_push_overwrite.txt b/src/docs/path_buf_push_overwrite.txt deleted file mode 100644 index 34f8901da239..000000000000 --- a/src/docs/path_buf_push_overwrite.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) -calls on `PathBuf` that can cause overwrites. - -### Why is this bad? -Calling `push` with a root path at the start can overwrite the -previous defined path. - -### Example -``` -use std::path::PathBuf; - -let mut x = PathBuf::from("/foo"); -x.push("/bar"); -assert_eq!(x, PathBuf::from("/bar")); -``` -Could be written: - -``` -use std::path::PathBuf; - -let mut x = PathBuf::from("/foo"); -x.push("bar"); -assert_eq!(x, PathBuf::from("/foo/bar")); -``` \ No newline at end of file diff --git a/src/docs/pattern_type_mismatch.txt b/src/docs/pattern_type_mismatch.txt deleted file mode 100644 index 64da881d592a..000000000000 --- a/src/docs/pattern_type_mismatch.txt +++ /dev/null @@ -1,64 +0,0 @@ -### What it does -Checks for patterns that aren't exact representations of the types -they are applied to. - -To satisfy this lint, you will have to adjust either the expression that is matched -against or the pattern itself, as well as the bindings that are introduced by the -adjusted patterns. For matching you will have to either dereference the expression -with the `*` operator, or amend the patterns to explicitly match against `&` -or `&mut ` depending on the reference mutability. For the bindings you need -to use the inverse. You can leave them as plain bindings if you wish for the value -to be copied, but you must use `ref mut ` or `ref ` to construct -a reference into the matched structure. - -If you are looking for a way to learn about ownership semantics in more detail, it -is recommended to look at IDE options available to you to highlight types, lifetimes -and reference semantics in your code. The available tooling would expose these things -in a general way even outside of the various pattern matching mechanics. Of course -this lint can still be used to highlight areas of interest and ensure a good understanding -of ownership semantics. - -### Why is this bad? -It isn't bad in general. But in some contexts it can be desirable -because it increases ownership hints in the code, and will guard against some changes -in ownership. - -### Example -This example shows the basic adjustments necessary to satisfy the lint. Note how -the matched expression is explicitly dereferenced with `*` and the `inner` variable -is bound to a shared borrow via `ref inner`. - -``` -// Bad -let value = &Some(Box::new(23)); -match value { - Some(inner) => println!("{}", inner), - None => println!("none"), -} - -// Good -let value = &Some(Box::new(23)); -match *value { - Some(ref inner) => println!("{}", inner), - None => println!("none"), -} -``` - -The following example demonstrates one of the advantages of the more verbose style. -Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable -borrow, while `b` is simply taken by value. This ensures that the loop body cannot -accidentally modify the wrong part of the structure. - -``` -// Bad -let mut values = vec![(2, 3), (3, 4)]; -for (a, b) in &mut values { - *a += *b; -} - -// Good -let mut values = vec![(2, 3), (3, 4)]; -for &mut (ref mut a, b) in &mut values { - *a += b; -} -``` \ No newline at end of file diff --git a/src/docs/possible_missing_comma.txt b/src/docs/possible_missing_comma.txt deleted file mode 100644 index 5d92f4cae91e..000000000000 --- a/src/docs/possible_missing_comma.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for possible missing comma in an array. It lints if -an array element is a binary operator expression and it lies on two lines. - -### Why is this bad? -This could lead to unexpected results. - -### Example -``` -let a = &[ - -1, -2, -3 // <= no comma here - -4, -5, -6 -]; -``` \ No newline at end of file diff --git a/src/docs/precedence.txt b/src/docs/precedence.txt deleted file mode 100644 index fda0b831f335..000000000000 --- a/src/docs/precedence.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for operations where precedence may be unclear -and suggests to add parentheses. Currently it catches the following: -* mixed usage of arithmetic and bit shifting/combining operators without -parentheses -* a "negative" numeric literal (which is really a unary `-` followed by a -numeric literal) - followed by a method call - -### Why is this bad? -Not everyone knows the precedence of those operators by -heart, so expressions like these may trip others trying to reason about the -code. - -### Example -* `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7 -* `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1 \ No newline at end of file diff --git a/src/docs/print_in_format_impl.txt b/src/docs/print_in_format_impl.txt deleted file mode 100644 index 140d23d6faab..000000000000 --- a/src/docs/print_in_format_impl.txt +++ /dev/null @@ -1,34 +0,0 @@ -### What it does -Checks for use of `println`, `print`, `eprintln` or `eprint` in an -implementation of a formatting trait. - -### Why is this bad? -Using a print macro is likely unintentional since formatting traits -should write to the `Formatter`, not stdout/stderr. - -### Example -``` -use std::fmt::{Display, Error, Formatter}; - -struct S; -impl Display for S { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - println!("S"); - - Ok(()) - } -} -``` -Use instead: -``` -use std::fmt::{Display, Error, Formatter}; - -struct S; -impl Display for S { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - writeln!(f, "S"); - - Ok(()) - } -} -``` \ No newline at end of file diff --git a/src/docs/print_literal.txt b/src/docs/print_literal.txt deleted file mode 100644 index a6252a68780b..000000000000 --- a/src/docs/print_literal.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns about the use of literals as `print!`/`println!` args. - -### Why is this bad? -Using literals as `println!` args is inefficient -(c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary -(i.e., just put the literal in the format string) - -### Example -``` -println!("{}", "foo"); -``` -use the literal without formatting: -``` -println!("foo"); -``` \ No newline at end of file diff --git a/src/docs/print_stderr.txt b/src/docs/print_stderr.txt deleted file mode 100644 index 9c6edeeef125..000000000000 --- a/src/docs/print_stderr.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for printing on *stderr*. The purpose of this lint -is to catch debugging remnants. - -### Why is this bad? -People often print on *stderr* while debugging an -application and might forget to remove those prints afterward. - -### Known problems -Only catches `eprint!` and `eprintln!` calls. - -### Example -``` -eprintln!("Hello world!"); -``` \ No newline at end of file diff --git a/src/docs/print_stdout.txt b/src/docs/print_stdout.txt deleted file mode 100644 index d2cbd811d1b2..000000000000 --- a/src/docs/print_stdout.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for printing on *stdout*. The purpose of this lint -is to catch debugging remnants. - -### Why is this bad? -People often print on *stdout* while debugging an -application and might forget to remove those prints afterward. - -### Known problems -Only catches `print!` and `println!` calls. - -### Example -``` -println!("Hello world!"); -``` \ No newline at end of file diff --git a/src/docs/print_with_newline.txt b/src/docs/print_with_newline.txt deleted file mode 100644 index 640323e822db..000000000000 --- a/src/docs/print_with_newline.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns when you use `print!()` with a format -string that ends in a newline. - -### Why is this bad? -You should use `println!()` instead, which appends the -newline. - -### Example -``` -print!("Hello {}!\n", name); -``` -use println!() instead -``` -println!("Hello {}!", name); -``` \ No newline at end of file diff --git a/src/docs/println_empty_string.txt b/src/docs/println_empty_string.txt deleted file mode 100644 index b980413022ca..000000000000 --- a/src/docs/println_empty_string.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns when you use `println!("")` to -print a newline. - -### Why is this bad? -You should use `println!()`, which is simpler. - -### Example -``` -println!(""); -``` - -Use instead: -``` -println!(); -``` \ No newline at end of file diff --git a/src/docs/ptr_arg.txt b/src/docs/ptr_arg.txt deleted file mode 100644 index 796b0a65b717..000000000000 --- a/src/docs/ptr_arg.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -This lint checks for function arguments of type `&String`, `&Vec`, -`&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls -with the appropriate `.to_owned()`/`to_string()` calls. - -### Why is this bad? -Requiring the argument to be of the specific size -makes the function less useful for no benefit; slices in the form of `&[T]` -or `&str` usually suffice and can be obtained from other types, too. - -### Known problems -There may be `fn(&Vec)`-typed references pointing to your function. -If you have them, you will get a compiler error after applying this lint's -suggestions. You then have the choice to undo your changes or change the -type of the reference. - -Note that if the function is part of your public interface, there may be -other crates referencing it, of which you may not be aware. Carefully -deprecate the function before applying the lint suggestions in this case. - -### Example -``` -fn foo(&Vec) { .. } -``` - -Use instead: -``` -fn foo(&[u32]) { .. } -``` \ No newline at end of file diff --git a/src/docs/ptr_as_ptr.txt b/src/docs/ptr_as_ptr.txt deleted file mode 100644 index 8fb35c4aae8f..000000000000 --- a/src/docs/ptr_as_ptr.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for `as` casts between raw pointers without changing its mutability, -namely `*const T` to `*const U` and `*mut T` to `*mut U`. - -### Why is this bad? -Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because -it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. - -### Example -``` -let ptr: *const u32 = &42_u32; -let mut_ptr: *mut u32 = &mut 42_u32; -let _ = ptr as *const i32; -let _ = mut_ptr as *mut i32; -``` -Use instead: -``` -let ptr: *const u32 = &42_u32; -let mut_ptr: *mut u32 = &mut 42_u32; -let _ = ptr.cast::(); -let _ = mut_ptr.cast::(); -``` \ No newline at end of file diff --git a/src/docs/ptr_eq.txt b/src/docs/ptr_eq.txt deleted file mode 100644 index 06b36ca55e69..000000000000 --- a/src/docs/ptr_eq.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Use `std::ptr::eq` when applicable - -### Why is this bad? -`ptr::eq` can be used to compare `&T` references -(which coerce to `*const T` implicitly) by their address rather than -comparing the values they point to. - -### Example -``` -let a = &[1, 2, 3]; -let b = &[1, 2, 3]; - -assert!(a as *const _ as usize == b as *const _ as usize); -``` -Use instead: -``` -let a = &[1, 2, 3]; -let b = &[1, 2, 3]; - -assert!(std::ptr::eq(a, b)); -``` \ No newline at end of file diff --git a/src/docs/ptr_offset_with_cast.txt b/src/docs/ptr_offset_with_cast.txt deleted file mode 100644 index f204e769bf4b..000000000000 --- a/src/docs/ptr_offset_with_cast.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for usage of the `offset` pointer method with a `usize` casted to an -`isize`. - -### Why is this bad? -If we’re always increasing the pointer address, we can avoid the numeric -cast by using the `add` method instead. - -### Example -``` -let vec = vec![b'a', b'b', b'c']; -let ptr = vec.as_ptr(); -let offset = 1_usize; - -unsafe { - ptr.offset(offset as isize); -} -``` - -Could be written: - -``` -let vec = vec![b'a', b'b', b'c']; -let ptr = vec.as_ptr(); -let offset = 1_usize; - -unsafe { - ptr.add(offset); -} -``` \ No newline at end of file diff --git a/src/docs/pub_use.txt b/src/docs/pub_use.txt deleted file mode 100644 index 407cafa01903..000000000000 --- a/src/docs/pub_use.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does - -Restricts the usage of `pub use ...` - -### Why is this bad? - -`pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent -unintentional exports or to encourage placing exported items directly in public modules - -### Example -``` -pub mod outer { - mod inner { - pub struct Test {} - } - pub use inner::Test; -} - -use outer::Test; -``` -Use instead: -``` -pub mod outer { - pub struct Test {} -} - -use outer::Test; -``` \ No newline at end of file diff --git a/src/docs/question_mark.txt b/src/docs/question_mark.txt deleted file mode 100644 index 4dc987be8813..000000000000 --- a/src/docs/question_mark.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for expressions that could be replaced by the question mark operator. - -### Why is this bad? -Question mark usage is more idiomatic. - -### Example -``` -if option.is_none() { - return None; -} -``` - -Could be written: - -``` -option?; -``` \ No newline at end of file diff --git a/src/docs/range_minus_one.txt b/src/docs/range_minus_one.txt deleted file mode 100644 index fcb96dcc34ed..000000000000 --- a/src/docs/range_minus_one.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for inclusive ranges where 1 is subtracted from -the upper bound, e.g., `x..=(y-1)`. - -### Why is this bad? -The code is more readable with an exclusive range -like `x..y`. - -### Known problems -This will cause a warning that cannot be fixed if -the consumer of the range only accepts a specific range type, instead of -the generic `RangeBounds` trait -([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). - -### Example -``` -for i in x..=(y-1) { - // .. -} -``` - -Use instead: -``` -for i in x..y { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/range_plus_one.txt b/src/docs/range_plus_one.txt deleted file mode 100644 index 193c85f9cbc7..000000000000 --- a/src/docs/range_plus_one.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks for exclusive ranges where 1 is added to the -upper bound, e.g., `x..(y+1)`. - -### Why is this bad? -The code is more readable with an inclusive range -like `x..=y`. - -### Known problems -Will add unnecessary pair of parentheses when the -expression is not wrapped in a pair but starts with an opening parenthesis -and ends with a closing one. -I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`. - -Also in many cases, inclusive ranges are still slower to run than -exclusive ranges, because they essentially add an extra branch that -LLVM may fail to hoist out of the loop. - -This will cause a warning that cannot be fixed if the consumer of the -range only accepts a specific range type, instead of the generic -`RangeBounds` trait -([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). - -### Example -``` -for i in x..(y+1) { - // .. -} -``` - -Use instead: -``` -for i in x..=y { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/range_zip_with_len.txt b/src/docs/range_zip_with_len.txt deleted file mode 100644 index 24c1efec789a..000000000000 --- a/src/docs/range_zip_with_len.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for zipping a collection with the range of -`0.._.len()`. - -### Why is this bad? -The code is better expressed with `.enumerate()`. - -### Example -``` -let _ = x.iter().zip(0..x.len()); -``` - -Use instead: -``` -let _ = x.iter().enumerate(); -``` \ No newline at end of file diff --git a/src/docs/rc_buffer.txt b/src/docs/rc_buffer.txt deleted file mode 100644 index 82ac58eeb308..000000000000 --- a/src/docs/rc_buffer.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`. - -### Why is this bad? -Expressions such as `Rc` usually have no advantage over `Rc`, since -it is larger and involves an extra level of indirection, and doesn't implement `Borrow`. - -While mutating a buffer type would still be possible with `Rc::get_mut()`, it only -works if there are no additional references yet, which usually defeats the purpose of -enclosing it in a shared ownership type. Instead, additionally wrapping the inner -type with an interior mutable container (such as `RefCell` or `Mutex`) would normally -be used. - -### Known problems -This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for -cases where mutation only happens before there are any additional references. - -### Example -``` -fn foo(interned: Rc) { ... } -``` - -Better: - -``` -fn foo(interned: Rc) { ... } -``` \ No newline at end of file diff --git a/src/docs/rc_clone_in_vec_init.txt b/src/docs/rc_clone_in_vec_init.txt deleted file mode 100644 index 6fc08aaf9ab5..000000000000 --- a/src/docs/rc_clone_in_vec_init.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`) -in `vec![elem; len]` - -### Why is this bad? -This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak` -is a bit misleading, as it will create references to the same pointer, rather -than different instances. - -### Example -``` -let v = vec![std::sync::Arc::new("some data".to_string()); 100]; -// or -let v = vec![std::rc::Rc::new("some data".to_string()); 100]; -``` -Use instead: -``` -// Initialize each value separately: -let mut data = Vec::with_capacity(100); -for _ in 0..100 { - data.push(std::rc::Rc::new("some data".to_string())); -} - -// Or if you want clones of the same reference, -// Create the reference beforehand to clarify that -// it should be cloned for each value -let data = std::rc::Rc::new("some data".to_string()); -let v = vec![data; 100]; -``` \ No newline at end of file diff --git a/src/docs/rc_mutex.txt b/src/docs/rc_mutex.txt deleted file mode 100644 index ed7a1e344d08..000000000000 --- a/src/docs/rc_mutex.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for `Rc>`. - -### Why is this bad? -`Rc` is used in single thread and `Mutex` is used in multi thread. -Consider using `Rc>` in single thread or `Arc>` in multi thread. - -### Known problems -Sometimes combining generic types can lead to the requirement that a -type use Rc in conjunction with Mutex. We must consider those cases false positives, but -alas they are quite hard to rule out. Luckily they are also rare. - -### Example -``` -use std::rc::Rc; -use std::sync::Mutex; -fn foo(interned: Rc>) { ... } -``` - -Better: - -``` -use std::rc::Rc; -use std::cell::RefCell -fn foo(interned: Rc>) { ... } -``` \ No newline at end of file diff --git a/src/docs/read_zero_byte_vec.txt b/src/docs/read_zero_byte_vec.txt deleted file mode 100644 index cef5604e01c0..000000000000 --- a/src/docs/read_zero_byte_vec.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -This lint catches reads into a zero-length `Vec`. -Especially in the case of a call to `with_capacity`, this lint warns that read -gets the number of bytes from the `Vec`'s length, not its capacity. - -### Why is this bad? -Reading zero bytes is almost certainly not the intended behavior. - -### Known problems -In theory, a very unusual read implementation could assign some semantic meaning -to zero-byte reads. But it seems exceptionally unlikely that code intending to do -a zero-byte read would allocate a `Vec` for it. - -### Example -``` -use std::io; -fn foo(mut f: F) { - let mut data = Vec::with_capacity(100); - f.read(&mut data).unwrap(); -} -``` -Use instead: -``` -use std::io; -fn foo(mut f: F) { - let mut data = Vec::with_capacity(100); - data.resize(100, 0); - f.read(&mut data).unwrap(); -} -``` \ No newline at end of file diff --git a/src/docs/recursive_format_impl.txt b/src/docs/recursive_format_impl.txt deleted file mode 100644 index 32fffd84cf43..000000000000 --- a/src/docs/recursive_format_impl.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for format trait implementations (e.g. `Display`) with a recursive call to itself -which uses `self` as a parameter. -This is typically done indirectly with the `write!` macro or with `to_string()`. - -### Why is this bad? -This will lead to infinite recursion and a stack overflow. - -### Example - -``` -use std::fmt; - -struct Structure(i32); -impl fmt::Display for Structure { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_string()) - } -} - -``` -Use instead: -``` -use std::fmt; - -struct Structure(i32); -impl fmt::Display for Structure { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} -``` \ No newline at end of file diff --git a/src/docs/redundant_allocation.txt b/src/docs/redundant_allocation.txt deleted file mode 100644 index 86bf51e8dfec..000000000000 --- a/src/docs/redundant_allocation.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for use of redundant allocations anywhere in the code. - -### Why is this bad? -Expressions such as `Rc<&T>`, `Rc>`, `Rc>`, `Rc>`, `Arc<&T>`, `Arc>`, -`Arc>`, `Arc>`, `Box<&T>`, `Box>`, `Box>`, `Box>`, add an unnecessary level of indirection. - -### Example -``` -fn foo(bar: Rc<&usize>) {} -``` - -Better: - -``` -fn foo(bar: &usize) {} -``` \ No newline at end of file diff --git a/src/docs/redundant_clone.txt b/src/docs/redundant_clone.txt deleted file mode 100644 index b29aed0b5e77..000000000000 --- a/src/docs/redundant_clone.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for a redundant `clone()` (and its relatives) which clones an owned -value that is going to be dropped without further use. - -### Why is this bad? -It is not always possible for the compiler to eliminate useless -allocations and deallocations generated by redundant `clone()`s. - -### Known problems -False-negatives: analysis performed by this lint is conservative and limited. - -### Example -``` -{ - let x = Foo::new(); - call(x.clone()); - call(x.clone()); // this can just pass `x` -} - -["lorem", "ipsum"].join(" ").to_string(); - -Path::new("/a/b").join("c").to_path_buf(); -``` \ No newline at end of file diff --git a/src/docs/redundant_closure.txt b/src/docs/redundant_closure.txt deleted file mode 100644 index 0faa9513f97f..000000000000 --- a/src/docs/redundant_closure.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for closures which just call another function where -the function can be called directly. `unsafe` functions or calls where types -get adjusted are ignored. - -### Why is this bad? -Needlessly creating a closure adds code for no benefit -and gives the optimizer more work. - -### Known problems -If creating the closure inside the closure has a side- -effect then moving the closure creation out will change when that side- -effect runs. -See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details. - -### Example -``` -xs.map(|x| foo(x)) -``` - -Use instead: -``` -// where `foo(_)` is a plain function that takes the exact argument type of `x`. -xs.map(foo) -``` \ No newline at end of file diff --git a/src/docs/redundant_closure_call.txt b/src/docs/redundant_closure_call.txt deleted file mode 100644 index 913d1a705e29..000000000000 --- a/src/docs/redundant_closure_call.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Detects closures called in the same expression where they -are defined. - -### Why is this bad? -It is unnecessarily adding to the expression's -complexity. - -### Example -``` -let a = (|| 42)(); -``` - -Use instead: -``` -let a = 42; -``` \ No newline at end of file diff --git a/src/docs/redundant_closure_for_method_calls.txt b/src/docs/redundant_closure_for_method_calls.txt deleted file mode 100644 index 865510e14755..000000000000 --- a/src/docs/redundant_closure_for_method_calls.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for closures which only invoke a method on the closure -argument and can be replaced by referencing the method directly. - -### Why is this bad? -It's unnecessary to create the closure. - -### Example -``` -Some('a').map(|s| s.to_uppercase()); -``` -may be rewritten as -``` -Some('a').map(char::to_uppercase); -``` \ No newline at end of file diff --git a/src/docs/redundant_else.txt b/src/docs/redundant_else.txt deleted file mode 100644 index 3f4e86917603..000000000000 --- a/src/docs/redundant_else.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for `else` blocks that can be removed without changing semantics. - -### Why is this bad? -The `else` block adds unnecessary indentation and verbosity. - -### Known problems -Some may prefer to keep the `else` block for clarity. - -### Example -``` -fn my_func(count: u32) { - if count == 0 { - print!("Nothing to do"); - return; - } else { - print!("Moving on..."); - } -} -``` -Use instead: -``` -fn my_func(count: u32) { - if count == 0 { - print!("Nothing to do"); - return; - } - print!("Moving on..."); -} -``` \ No newline at end of file diff --git a/src/docs/redundant_feature_names.txt b/src/docs/redundant_feature_names.txt deleted file mode 100644 index 5bd6925ed47d..000000000000 --- a/src/docs/redundant_feature_names.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for feature names with prefix `use-`, `with-` or suffix `-support` - -### Why is this bad? -These prefixes and suffixes have no significant meaning. - -### Example -``` -[features] -default = ["use-abc", "with-def", "ghi-support"] -use-abc = [] // redundant -with-def = [] // redundant -ghi-support = [] // redundant -``` - -Use instead: -``` -[features] -default = ["abc", "def", "ghi"] -abc = [] -def = [] -ghi = [] -``` diff --git a/src/docs/redundant_field_names.txt b/src/docs/redundant_field_names.txt deleted file mode 100644 index 35f20a466b37..000000000000 --- a/src/docs/redundant_field_names.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for fields in struct literals where shorthands -could be used. - -### Why is this bad? -If the field and variable names are the same, -the field name is redundant. - -### Example -``` -let bar: u8 = 123; - -struct Foo { - bar: u8, -} - -let foo = Foo { bar: bar }; -``` -the last line can be simplified to -``` -let foo = Foo { bar }; -``` \ No newline at end of file diff --git a/src/docs/redundant_pattern.txt b/src/docs/redundant_pattern.txt deleted file mode 100644 index 45f6cfc86702..000000000000 --- a/src/docs/redundant_pattern.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for patterns in the form `name @ _`. - -### Why is this bad? -It's almost always more readable to just use direct -bindings. - -### Example -``` -match v { - Some(x) => (), - y @ _ => (), -} -``` - -Use instead: -``` -match v { - Some(x) => (), - y => (), -} -``` \ No newline at end of file diff --git a/src/docs/redundant_pattern_matching.txt b/src/docs/redundant_pattern_matching.txt deleted file mode 100644 index 77b1021e0db3..000000000000 --- a/src/docs/redundant_pattern_matching.txt +++ /dev/null @@ -1,45 +0,0 @@ -### What it does -Lint for redundant pattern matching over `Result`, `Option`, -`std::task::Poll` or `std::net::IpAddr` - -### Why is this bad? -It's more concise and clear to just use the proper -utility function - -### Known problems -This will change the drop order for the matched type. Both `if let` and -`while let` will drop the value at the end of the block, both `if` and `while` will drop the -value before entering the block. For most types this change will not matter, but for a few -types this will not be an acceptable change (e.g. locks). See the -[reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about -drop order. - -### Example -``` -if let Ok(_) = Ok::(42) {} -if let Err(_) = Err::(42) {} -if let None = None::<()> {} -if let Some(_) = Some(42) {} -if let Poll::Pending = Poll::Pending::<()> {} -if let Poll::Ready(_) = Poll::Ready(42) {} -if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {} -if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {} -match Ok::(42) { - Ok(_) => true, - Err(_) => false, -}; -``` - -The more idiomatic use would be: - -``` -if Ok::(42).is_ok() {} -if Err::(42).is_err() {} -if None::<()>.is_none() {} -if Some(42).is_some() {} -if Poll::Pending::<()>.is_pending() {} -if Poll::Ready(42).is_ready() {} -if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {} -if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {} -Ok::(42).is_ok(); -``` \ No newline at end of file diff --git a/src/docs/redundant_pub_crate.txt b/src/docs/redundant_pub_crate.txt deleted file mode 100644 index a527bb5acda1..000000000000 --- a/src/docs/redundant_pub_crate.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for items declared `pub(crate)` that are not crate visible because they -are inside a private module. - -### Why is this bad? -Writing `pub(crate)` is misleading when it's redundant due to the parent -module's visibility. - -### Example -``` -mod internal { - pub(crate) fn internal_fn() { } -} -``` -This function is not visible outside the module and it can be declared with `pub` or -private visibility -``` -mod internal { - pub fn internal_fn() { } -} -``` \ No newline at end of file diff --git a/src/docs/redundant_slicing.txt b/src/docs/redundant_slicing.txt deleted file mode 100644 index 6798911ed76c..000000000000 --- a/src/docs/redundant_slicing.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for redundant slicing expressions which use the full range, and -do not change the type. - -### Why is this bad? -It unnecessarily adds complexity to the expression. - -### Known problems -If the type being sliced has an implementation of `Index` -that actually changes anything then it can't be removed. However, this would be surprising -to people reading the code and should have a note with it. - -### Example -``` -fn get_slice(x: &[u32]) -> &[u32] { - &x[..] -} -``` -Use instead: -``` -fn get_slice(x: &[u32]) -> &[u32] { - x -} -``` \ No newline at end of file diff --git a/src/docs/redundant_static_lifetimes.txt b/src/docs/redundant_static_lifetimes.txt deleted file mode 100644 index edb8e7b5c62b..000000000000 --- a/src/docs/redundant_static_lifetimes.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for constants and statics with an explicit `'static` lifetime. - -### Why is this bad? -Adding `'static` to every reference can create very -complicated types. - -### Example -``` -const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] = -&[...] -static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] = -&[...] -``` -This code can be rewritten as -``` - const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...] - static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...] -``` \ No newline at end of file diff --git a/src/docs/ref_binding_to_reference.txt b/src/docs/ref_binding_to_reference.txt deleted file mode 100644 index dc391cd988e5..000000000000 --- a/src/docs/ref_binding_to_reference.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for `ref` bindings which create a reference to a reference. - -### Why is this bad? -The address-of operator at the use site is clearer about the need for a reference. - -### Example -``` -let x = Some(""); -if let Some(ref x) = x { - // use `x` here -} -``` - -Use instead: -``` -let x = Some(""); -if let Some(x) = x { - // use `&x` here -} -``` \ No newline at end of file diff --git a/src/docs/ref_option_ref.txt b/src/docs/ref_option_ref.txt deleted file mode 100644 index 951c7bd7f7ec..000000000000 --- a/src/docs/ref_option_ref.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `&Option<&T>`. - -### Why is this bad? -Since `&` is Copy, it's useless to have a -reference on `Option<&T>`. - -### Known problems -It may be irrelevant to use this lint on -public API code as it will make a breaking change to apply it. - -### Example -``` -let x: &Option<&u32> = &Some(&0u32); -``` -Use instead: -``` -let x: Option<&u32> = Some(&0u32); -``` \ No newline at end of file diff --git a/src/docs/repeat_once.txt b/src/docs/repeat_once.txt deleted file mode 100644 index 3ba189c1945d..000000000000 --- a/src/docs/repeat_once.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for usage of `.repeat(1)` and suggest the following method for each types. -- `.to_string()` for `str` -- `.clone()` for `String` -- `.to_vec()` for `slice` - -The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if -they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306)) - -### Why is this bad? -For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning -the string is the intention behind this, `clone()` should be used. - -### Example -``` -fn main() { - let x = String::from("hello world").repeat(1); -} -``` -Use instead: -``` -fn main() { - let x = String::from("hello world").clone(); -} -``` \ No newline at end of file diff --git a/src/docs/rest_pat_in_fully_bound_structs.txt b/src/docs/rest_pat_in_fully_bound_structs.txt deleted file mode 100644 index 40ebbe754a37..000000000000 --- a/src/docs/rest_pat_in_fully_bound_structs.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched. - -### Why is this bad? -Correctness and readability. It's like having a wildcard pattern after -matching all enum variants explicitly. - -### Example -``` -let a = A { a: 5 }; - -match a { - A { a: 5, .. } => {}, - _ => {}, -} -``` - -Use instead: -``` -match a { - A { a: 5 } => {}, - _ => {}, -} -``` \ No newline at end of file diff --git a/src/docs/result_large_err.txt b/src/docs/result_large_err.txt deleted file mode 100644 index e5fab3c5cfcf..000000000000 --- a/src/docs/result_large_err.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks for functions that return `Result` with an unusually large -`Err`-variant. - -### Why is this bad? -A `Result` is at least as large as the `Err`-variant. While we -expect that variant to be seldomly used, the compiler needs to reserve -and move that much memory every single time. - -### Known problems -The size determined by Clippy is platform-dependent. - -### Examples -``` -pub enum ParseError { - UnparsedBytes([u8; 512]), - UnexpectedEof, -} - -// The `Result` has at least 512 bytes, even in the `Ok`-case -pub fn parse() -> Result<(), ParseError> { - Ok(()) -} -``` -should be -``` -pub enum ParseError { - UnparsedBytes(Box<[u8; 512]>), - UnexpectedEof, -} - -// The `Result` is slightly larger than a pointer -pub fn parse() -> Result<(), ParseError> { - Ok(()) -} -``` \ No newline at end of file diff --git a/src/docs/result_map_or_into_option.txt b/src/docs/result_map_or_into_option.txt deleted file mode 100644 index 899d98c307c8..000000000000 --- a/src/docs/result_map_or_into_option.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `_.map_or(None, Some)`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.ok()`. - -### Example -``` -assert_eq!(Some(1), r.map_or(None, Some)); -``` - -Use instead: -``` -assert_eq!(Some(1), r.ok()); -``` \ No newline at end of file diff --git a/src/docs/result_map_unit_fn.txt b/src/docs/result_map_unit_fn.txt deleted file mode 100644 index 3455c5c1f9b8..000000000000 --- a/src/docs/result_map_unit_fn.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for usage of `result.map(f)` where f is a function -or closure that returns the unit type `()`. - -### Why is this bad? -Readability, this can be written more clearly with -an if let statement - -### Example -``` -let x: Result = do_stuff(); -x.map(log_err_msg); -x.map(|msg| log_err_msg(format_msg(msg))); -``` - -The correct use would be: - -``` -let x: Result = do_stuff(); -if let Ok(msg) = x { - log_err_msg(msg); -}; -if let Ok(msg) = x { - log_err_msg(format_msg(msg)); -}; -``` \ No newline at end of file diff --git a/src/docs/result_unit_err.txt b/src/docs/result_unit_err.txt deleted file mode 100644 index 7c8ec2ffcf94..000000000000 --- a/src/docs/result_unit_err.txt +++ /dev/null @@ -1,40 +0,0 @@ -### What it does -Checks for public functions that return a `Result` -with an `Err` type of `()`. It suggests using a custom type that -implements `std::error::Error`. - -### Why is this bad? -Unit does not implement `Error` and carries no -further information about what went wrong. - -### Known problems -Of course, this lint assumes that `Result` is used -for a fallible operation (which is after all the intended use). However -code may opt to (mis)use it as a basic two-variant-enum. In that case, -the suggestion is misguided, and the code should use a custom enum -instead. - -### Examples -``` -pub fn read_u8() -> Result { Err(()) } -``` -should become -``` -use std::fmt; - -#[derive(Debug)] -pub struct EndOfStream; - -impl fmt::Display for EndOfStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "End of Stream") - } -} - -impl std::error::Error for EndOfStream { } - -pub fn read_u8() -> Result { Err(EndOfStream) } -``` - -Note that there are crates that simplify creating the error type, e.g. -[`thiserror`](https://docs.rs/thiserror). \ No newline at end of file diff --git a/src/docs/return_self_not_must_use.txt b/src/docs/return_self_not_must_use.txt deleted file mode 100644 index 4a4fd2c6e517..000000000000 --- a/src/docs/return_self_not_must_use.txt +++ /dev/null @@ -1,46 +0,0 @@ -### What it does -This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute. - -### Why is this bad? -Methods returning `Self` often create new values, having the `#[must_use]` attribute -prevents users from "forgetting" to use the newly created value. - -The `#[must_use]` attribute can be added to the type itself to ensure that instances -are never forgotten. Functions returning a type marked with `#[must_use]` will not be -linted, as the usage is already enforced by the type attribute. - -### Limitations -This lint is only applied on methods taking a `self` argument. It would be mostly noise -if it was added on constructors for example. - -### Example -``` -pub struct Bar; -impl Bar { - // Missing attribute - pub fn bar(&self) -> Self { - Self - } -} -``` - -Use instead: -``` -// It's better to have the `#[must_use]` attribute on the method like this: -pub struct Bar; -impl Bar { - #[must_use] - pub fn bar(&self) -> Self { - Self - } -} - -// Or on the type definition like this: -#[must_use] -pub struct Bar; -impl Bar { - pub fn bar(&self) -> Self { - Self - } -} -``` \ No newline at end of file diff --git a/src/docs/reversed_empty_ranges.txt b/src/docs/reversed_empty_ranges.txt deleted file mode 100644 index 39f481193866..000000000000 --- a/src/docs/reversed_empty_ranges.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for range expressions `x..y` where both `x` and `y` -are constant and `x` is greater or equal to `y`. - -### Why is this bad? -Empty ranges yield no values so iterating them is a no-op. -Moreover, trying to use a reversed range to index a slice will panic at run-time. - -### Example -``` -fn main() { - (10..=0).for_each(|x| println!("{}", x)); - - let arr = [1, 2, 3, 4, 5]; - let sub = &arr[3..1]; -} -``` -Use instead: -``` -fn main() { - (0..=10).rev().for_each(|x| println!("{}", x)); - - let arr = [1, 2, 3, 4, 5]; - let sub = &arr[1..3]; -} -``` \ No newline at end of file diff --git a/src/docs/same_functions_in_if_condition.txt b/src/docs/same_functions_in_if_condition.txt deleted file mode 100644 index a0a90eec681e..000000000000 --- a/src/docs/same_functions_in_if_condition.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Checks for consecutive `if`s with the same function call. - -### Why is this bad? -This is probably a copy & paste error. -Despite the fact that function can have side effects and `if` works as -intended, such an approach is implicit and can be considered a "code smell". - -### Example -``` -if foo() == bar { - … -} else if foo() == bar { - … -} -``` - -This probably should be: -``` -if foo() == bar { - … -} else if foo() == baz { - … -} -``` - -or if the original code was not a typo and called function mutates a state, -consider move the mutation out of the `if` condition to avoid similarity to -a copy & paste error: - -``` -let first = foo(); -if first == bar { - … -} else { - let second = foo(); - if second == bar { - … - } -} -``` \ No newline at end of file diff --git a/src/docs/same_item_push.txt b/src/docs/same_item_push.txt deleted file mode 100644 index 7e724073f8aa..000000000000 --- a/src/docs/same_item_push.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks whether a for loop is being used to push a constant -value into a Vec. - -### Why is this bad? -This kind of operation can be expressed more succinctly with -`vec![item; SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also -have better performance. - -### Example -``` -let item1 = 2; -let item2 = 3; -let mut vec: Vec = Vec::new(); -for _ in 0..20 { - vec.push(item1); -} -for _ in 0..30 { - vec.push(item2); -} -``` - -Use instead: -``` -let item1 = 2; -let item2 = 3; -let mut vec: Vec = vec![item1; 20]; -vec.resize(20 + 30, item2); -``` \ No newline at end of file diff --git a/src/docs/same_name_method.txt b/src/docs/same_name_method.txt deleted file mode 100644 index 792dd717fc64..000000000000 --- a/src/docs/same_name_method.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -It lints if a struct has two methods with the same name: -one from a trait, another not from trait. - -### Why is this bad? -Confusing. - -### Example -``` -trait T { - fn foo(&self) {} -} - -struct S; - -impl T for S { - fn foo(&self) {} -} - -impl S { - fn foo(&self) {} -} -``` \ No newline at end of file diff --git a/src/docs/search_is_some.txt b/src/docs/search_is_some.txt deleted file mode 100644 index 67292d54585f..000000000000 --- a/src/docs/search_is_some.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for an iterator or string search (such as `find()`, -`position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`. - -### Why is this bad? -Readability, this can be written more concisely as: -* `_.any(_)`, or `_.contains(_)` for `is_some()`, -* `!_.any(_)`, or `!_.contains(_)` for `is_none()`. - -### Example -``` -let vec = vec![1]; -vec.iter().find(|x| **x == 0).is_some(); - -"hello world".find("world").is_none(); -``` - -Use instead: -``` -let vec = vec![1]; -vec.iter().any(|x| *x == 0); - -!"hello world".contains("world"); -``` \ No newline at end of file diff --git a/src/docs/self_assignment.txt b/src/docs/self_assignment.txt deleted file mode 100644 index ea60ea077ab5..000000000000 --- a/src/docs/self_assignment.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for explicit self-assignments. - -### Why is this bad? -Self-assignments are redundant and unlikely to be -intentional. - -### Known problems -If expression contains any deref coercions or -indexing operations they are assumed not to have any side effects. - -### Example -``` -struct Event { - x: i32, -} - -fn copy_position(a: &mut Event, b: &Event) { - a.x = a.x; -} -``` - -Should be: -``` -struct Event { - x: i32, -} - -fn copy_position(a: &mut Event, b: &Event) { - a.x = b.x; -} -``` \ No newline at end of file diff --git a/src/docs/self_named_constructors.txt b/src/docs/self_named_constructors.txt deleted file mode 100644 index a01669a84542..000000000000 --- a/src/docs/self_named_constructors.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Warns when constructors have the same name as their types. - -### Why is this bad? -Repeating the name of the type is redundant. - -### Example -``` -struct Foo {} - -impl Foo { - pub fn foo() -> Foo { - Foo {} - } -} -``` -Use instead: -``` -struct Foo {} - -impl Foo { - pub fn new() -> Foo { - Foo {} - } -} -``` \ No newline at end of file diff --git a/src/docs/self_named_module_files.txt b/src/docs/self_named_module_files.txt deleted file mode 100644 index 73e805136288..000000000000 --- a/src/docs/self_named_module_files.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks that module layout uses only `mod.rs` files. - -### Why is this bad? -Having multiple module layout styles in a project can be confusing. - -### Example -``` -src/ - stuff/ - stuff_files.rs - stuff.rs - lib.rs -``` -Use instead: -``` -src/ - stuff/ - stuff_files.rs - mod.rs - lib.rs -``` \ No newline at end of file diff --git a/src/docs/semicolon_if_nothing_returned.txt b/src/docs/semicolon_if_nothing_returned.txt deleted file mode 100644 index 30c963ca211b..000000000000 --- a/src/docs/semicolon_if_nothing_returned.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Looks for blocks of expressions and fires if the last expression returns -`()` but is not followed by a semicolon. - -### Why is this bad? -The semicolon might be optional but when extending the block with new -code, it doesn't require a change in previous last line. - -### Example -``` -fn main() { - println!("Hello world") -} -``` -Use instead: -``` -fn main() { - println!("Hello world"); -} -``` \ No newline at end of file diff --git a/src/docs/separated_literal_suffix.txt b/src/docs/separated_literal_suffix.txt deleted file mode 100644 index 226a6b8a9876..000000000000 --- a/src/docs/separated_literal_suffix.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Warns if literal suffixes are separated by an underscore. -To enforce separated literal suffix style, -see the `unseparated_literal_suffix` lint. - -### Why is this bad? -Suffix style should be consistent. - -### Example -``` -123832_i32 -``` - -Use instead: -``` -123832i32 -``` \ No newline at end of file diff --git a/src/docs/serde_api_misuse.txt b/src/docs/serde_api_misuse.txt deleted file mode 100644 index 8a3c89ac11ad..000000000000 --- a/src/docs/serde_api_misuse.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for mis-uses of the serde API. - -### Why is this bad? -Serde is very finnicky about how its API should be -used, but the type system can't be used to enforce it (yet?). - -### Example -Implementing `Visitor::visit_string` but not -`Visitor::visit_str`. \ No newline at end of file diff --git a/src/docs/shadow_reuse.txt b/src/docs/shadow_reuse.txt deleted file mode 100644 index 9eb8e7ad164b..000000000000 --- a/src/docs/shadow_reuse.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for bindings that shadow other bindings already in -scope, while reusing the original value. - -### Why is this bad? -Not too much, in fact it's a common pattern in Rust -code. Still, some argue that name shadowing like this hurts readability, -because a value may be bound to different things depending on position in -the code. - -### Example -``` -let x = 2; -let x = x + 1; -``` -use different variable name: -``` -let x = 2; -let y = x + 1; -``` \ No newline at end of file diff --git a/src/docs/shadow_same.txt b/src/docs/shadow_same.txt deleted file mode 100644 index 3cd96f560a5e..000000000000 --- a/src/docs/shadow_same.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for bindings that shadow other bindings already in -scope, while just changing reference level or mutability. - -### Why is this bad? -Not much, in fact it's a very common pattern in Rust -code. Still, some may opt to avoid it in their code base, they can set this -lint to `Warn`. - -### Example -``` -let x = &x; -``` - -Use instead: -``` -let y = &x; // use different variable name -``` \ No newline at end of file diff --git a/src/docs/shadow_unrelated.txt b/src/docs/shadow_unrelated.txt deleted file mode 100644 index 436251c520a9..000000000000 --- a/src/docs/shadow_unrelated.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for bindings that shadow other bindings already in -scope, either without an initialization or with one that does not even use -the original value. - -### Why is this bad? -Name shadowing can hurt readability, especially in -large code bases, because it is easy to lose track of the active binding at -any place in the code. This can be alleviated by either giving more specific -names to bindings or introducing more scopes to contain the bindings. - -### Example -``` -let x = y; -let x = z; // shadows the earlier binding -``` - -Use instead: -``` -let x = y; -let w = z; // use different variable name -``` \ No newline at end of file diff --git a/src/docs/short_circuit_statement.txt b/src/docs/short_circuit_statement.txt deleted file mode 100644 index 31492bed03d4..000000000000 --- a/src/docs/short_circuit_statement.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for the use of short circuit boolean conditions as -a -statement. - -### Why is this bad? -Using a short circuit boolean condition as a statement -may hide the fact that the second part is executed or not depending on the -outcome of the first part. - -### Example -``` -f() && g(); // We should write `if f() { g(); }`. -``` \ No newline at end of file diff --git a/src/docs/should_implement_trait.txt b/src/docs/should_implement_trait.txt deleted file mode 100644 index 02e74751ae03..000000000000 --- a/src/docs/should_implement_trait.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for methods that should live in a trait -implementation of a `std` trait (see [llogiq's blog -post](http://llogiq.github.io/2015/07/30/traits.html) for further -information) instead of an inherent implementation. - -### Why is this bad? -Implementing the traits improve ergonomics for users of -the code, often with very little cost. Also people seeing a `mul(...)` -method -may expect `*` to work equally, so you should have good reason to disappoint -them. - -### Example -``` -struct X; -impl X { - fn add(&self, other: &X) -> X { - // .. - } -} -``` \ No newline at end of file diff --git a/src/docs/significant_drop_in_scrutinee.txt b/src/docs/significant_drop_in_scrutinee.txt deleted file mode 100644 index f869def0ddb7..000000000000 --- a/src/docs/significant_drop_in_scrutinee.txt +++ /dev/null @@ -1,43 +0,0 @@ -### What it does -Check for temporaries returned from function calls in a match scrutinee that have the -`clippy::has_significant_drop` attribute. - -### Why is this bad? -The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have -an important side-effect, such as unlocking a mutex, making it important for users to be -able to accurately understand their lifetimes. When a temporary is returned in a function -call in a match scrutinee, its lifetime lasts until the end of the match block, which may -be surprising. - -For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a -function call that returns a `MutexGuard` and then tries to lock again in one of the match -arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of -the match block and thus will not unlock. - -### Example -``` -let mutex = Mutex::new(State {}); - -match mutex.lock().unwrap().foo() { - true => { - mutex.lock().unwrap().bar(); // Deadlock! - } - false => {} -}; - -println!("All done!"); -``` -Use instead: -``` -let mutex = Mutex::new(State {}); - -let is_foo = mutex.lock().unwrap().foo(); -match is_foo { - true => { - mutex.lock().unwrap().bar(); - } - false => {} -}; - -println!("All done!"); -``` \ No newline at end of file diff --git a/src/docs/similar_names.txt b/src/docs/similar_names.txt deleted file mode 100644 index f9eff21b6793..000000000000 --- a/src/docs/similar_names.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for names that are very similar and thus confusing. - -Note: this lint looks for similar names throughout each -scope. To allow it, you need to allow it on the scope -level, not on the name that is reported. - -### Why is this bad? -It's hard to distinguish between names that differ only -by a single character. - -### Example -``` -let checked_exp = something; -let checked_expr = something_else; -``` \ No newline at end of file diff --git a/src/docs/single_char_add_str.txt b/src/docs/single_char_add_str.txt deleted file mode 100644 index cf23dc0c89bb..000000000000 --- a/src/docs/single_char_add_str.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Warns when using `push_str`/`insert_str` with a single-character string literal -where `push`/`insert` with a `char` would work fine. - -### Why is this bad? -It's less clear that we are pushing a single character. - -### Example -``` -string.insert_str(0, "R"); -string.push_str("R"); -``` - -Use instead: -``` -string.insert(0, 'R'); -string.push('R'); -``` \ No newline at end of file diff --git a/src/docs/single_char_lifetime_names.txt b/src/docs/single_char_lifetime_names.txt deleted file mode 100644 index 92dd24bf247e..000000000000 --- a/src/docs/single_char_lifetime_names.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for lifetimes with names which are one character -long. - -### Why is this bad? -A single character is likely not enough to express the -purpose of a lifetime. Using a longer name can make code -easier to understand, especially for those who are new to -Rust. - -### Known problems -Rust programmers and learning resources tend to use single -character lifetimes, so this lint is at odds with the -ecosystem at large. In addition, the lifetime's purpose may -be obvious or, rarely, expressible in one character. - -### Example -``` -struct DiagnosticCtx<'a> { - source: &'a str, -} -``` -Use instead: -``` -struct DiagnosticCtx<'src> { - source: &'src str, -} -``` \ No newline at end of file diff --git a/src/docs/single_char_pattern.txt b/src/docs/single_char_pattern.txt deleted file mode 100644 index 9e5ad1e7d639..000000000000 --- a/src/docs/single_char_pattern.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for string methods that receive a single-character -`str` as an argument, e.g., `_.split("x")`. - -### Why is this bad? -Performing these methods using a `char` is faster than -using a `str`. - -### Known problems -Does not catch multi-byte unicode characters. - -### Example -``` -_.split("x"); -``` - -Use instead: -``` -_.split('x'); -``` \ No newline at end of file diff --git a/src/docs/single_component_path_imports.txt b/src/docs/single_component_path_imports.txt deleted file mode 100644 index 3a026388183e..000000000000 --- a/src/docs/single_component_path_imports.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checking for imports with single component use path. - -### Why is this bad? -Import with single component use path such as `use cratename;` -is not necessary, and thus should be removed. - -### Example -``` -use regex; - -fn main() { - regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); -} -``` -Better as -``` -fn main() { - regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); -} -``` \ No newline at end of file diff --git a/src/docs/single_element_loop.txt b/src/docs/single_element_loop.txt deleted file mode 100644 index 6f0c15a85f77..000000000000 --- a/src/docs/single_element_loop.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks whether a for loop has a single element. - -### Why is this bad? -There is no reason to have a loop of a -single element. - -### Example -``` -let item1 = 2; -for item in &[item1] { - println!("{}", item); -} -``` - -Use instead: -``` -let item1 = 2; -let item = &item1; -println!("{}", item); -``` \ No newline at end of file diff --git a/src/docs/single_match.txt b/src/docs/single_match.txt deleted file mode 100644 index 31dde4da8489..000000000000 --- a/src/docs/single_match.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for matches with a single arm where an `if let` -will usually suffice. - -### Why is this bad? -Just readability – `if let` nests less than a `match`. - -### Example -``` -match x { - Some(ref foo) => bar(foo), - _ => (), -} -``` - -Use instead: -``` -if let Some(ref foo) = x { - bar(foo); -} -``` \ No newline at end of file diff --git a/src/docs/single_match_else.txt b/src/docs/single_match_else.txt deleted file mode 100644 index 29a447af09b8..000000000000 --- a/src/docs/single_match_else.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for matches with two arms where an `if let else` will -usually suffice. - -### Why is this bad? -Just readability – `if let` nests less than a `match`. - -### Known problems -Personal style preferences may differ. - -### Example -Using `match`: - -``` -match x { - Some(ref foo) => bar(foo), - _ => bar(&other_ref), -} -``` - -Using `if let` with `else`: - -``` -if let Some(ref foo) = x { - bar(foo); -} else { - bar(&other_ref); -} -``` \ No newline at end of file diff --git a/src/docs/size_of_in_element_count.txt b/src/docs/size_of_in_element_count.txt deleted file mode 100644 index d893ec6a2a01..000000000000 --- a/src/docs/size_of_in_element_count.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Detects expressions where -`size_of::` or `size_of_val::` is used as a -count of elements of type `T` - -### Why is this bad? -These functions expect a count -of `T` and not a number of bytes - -### Example -``` -const SIZE: usize = 128; -let x = [2u8; SIZE]; -let mut y = [2u8; SIZE]; -unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; -``` \ No newline at end of file diff --git a/src/docs/skip_while_next.txt b/src/docs/skip_while_next.txt deleted file mode 100644 index 1ec8a3a28d5b..000000000000 --- a/src/docs/skip_while_next.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `_.skip_while(condition).next()`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.find(!condition)`. - -### Example -``` -vec.iter().skip_while(|x| **x == 0).next(); -``` - -Use instead: -``` -vec.iter().find(|x| **x != 0); -``` \ No newline at end of file diff --git a/src/docs/slow_vector_initialization.txt b/src/docs/slow_vector_initialization.txt deleted file mode 100644 index 53442e179654..000000000000 --- a/src/docs/slow_vector_initialization.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks slow zero-filled vector initialization - -### Why is this bad? -These structures are non-idiomatic and less efficient than simply using -`vec![0; len]`. - -### Example -``` -let mut vec1 = Vec::with_capacity(len); -vec1.resize(len, 0); - -let mut vec1 = Vec::with_capacity(len); -vec1.resize(vec1.capacity(), 0); - -let mut vec2 = Vec::with_capacity(len); -vec2.extend(repeat(0).take(len)); -``` - -Use instead: -``` -let mut vec1 = vec![0; len]; -let mut vec2 = vec![0; len]; -``` \ No newline at end of file diff --git a/src/docs/stable_sort_primitive.txt b/src/docs/stable_sort_primitive.txt deleted file mode 100644 index 6465dbee46b1..000000000000 --- a/src/docs/stable_sort_primitive.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -When sorting primitive values (integers, bools, chars, as well -as arrays, slices, and tuples of such items), it is typically better to -use an unstable sort than a stable sort. - -### Why is this bad? -Typically, using a stable sort consumes more memory and cpu cycles. -Because values which compare equal are identical, preserving their -relative order (the guarantee that a stable sort provides) means -nothing, while the extra costs still apply. - -### Known problems - -As pointed out in -[issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241), -a stable sort can instead be significantly faster for certain scenarios -(eg. when a sorted vector is extended with new data and resorted). - -For more information and benchmarking results, please refer to the -issue linked above. - -### Example -``` -let mut vec = vec![2, 1, 3]; -vec.sort(); -``` -Use instead: -``` -let mut vec = vec![2, 1, 3]; -vec.sort_unstable(); -``` \ No newline at end of file diff --git a/src/docs/std_instead_of_alloc.txt b/src/docs/std_instead_of_alloc.txt deleted file mode 100644 index c2d32704e505..000000000000 --- a/src/docs/std_instead_of_alloc.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does - -Finds items imported through `std` when available through `alloc`. - -### Why is this bad? - -Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from -alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful -for crates migrating to become `no_std` compatible. - -### Example -``` -use std::vec::Vec; -``` -Use instead: -``` -use alloc::vec::Vec; -``` \ No newline at end of file diff --git a/src/docs/std_instead_of_core.txt b/src/docs/std_instead_of_core.txt deleted file mode 100644 index f1e1518c6a62..000000000000 --- a/src/docs/std_instead_of_core.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does - -Finds items imported through `std` when available through `core`. - -### Why is this bad? - -Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure -disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates -migrating to become `no_std` compatible. - -### Example -``` -use std::hash::Hasher; -``` -Use instead: -``` -use core::hash::Hasher; -``` \ No newline at end of file diff --git a/src/docs/str_to_string.txt b/src/docs/str_to_string.txt deleted file mode 100644 index a24755223747..000000000000 --- a/src/docs/str_to_string.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -This lint checks for `.to_string()` method calls on values of type `&str`. - -### Why is this bad? -The `to_string` method is also used on other types to convert them to a string. -When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better -expressed with `.to_owned()`. - -### Example -``` -// example code where clippy issues a warning -let _ = "str".to_string(); -``` -Use instead: -``` -// example code which does not raise clippy warning -let _ = "str".to_owned(); -``` \ No newline at end of file diff --git a/src/docs/string_add.txt b/src/docs/string_add.txt deleted file mode 100644 index 23dafd0d0333..000000000000 --- a/src/docs/string_add.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for all instances of `x + _` where `x` is of type -`String`, but only if [`string_add_assign`](#string_add_assign) does *not* -match. - -### Why is this bad? -It's not bad in and of itself. However, this particular -`Add` implementation is asymmetric (the other operand need not be `String`, -but `x` does), while addition as mathematically defined is symmetric, also -the `String::push_str(_)` function is a perfectly good replacement. -Therefore, some dislike it and wish not to have it in their code. - -That said, other people think that string addition, having a long tradition -in other languages is actually fine, which is why we decided to make this -particular lint `allow` by default. - -### Example -``` -let x = "Hello".to_owned(); -x + ", World"; -``` - -Use instead: -``` -let mut x = "Hello".to_owned(); -x.push_str(", World"); -``` \ No newline at end of file diff --git a/src/docs/string_add_assign.txt b/src/docs/string_add_assign.txt deleted file mode 100644 index 7438be855db8..000000000000 --- a/src/docs/string_add_assign.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for string appends of the form `x = x + y` (without -`let`!). - -### Why is this bad? -It's not really bad, but some people think that the -`.push_str(_)` method is more readable. - -### Example -``` -let mut x = "Hello".to_owned(); -x = x + ", World"; - -// More readable -x += ", World"; -x.push_str(", World"); -``` \ No newline at end of file diff --git a/src/docs/string_extend_chars.txt b/src/docs/string_extend_chars.txt deleted file mode 100644 index 619ea3e11867..000000000000 --- a/src/docs/string_extend_chars.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for the use of `.extend(s.chars())` where s is a -`&str` or `String`. - -### Why is this bad? -`.push_str(s)` is clearer - -### Example -``` -let abc = "abc"; -let def = String::from("def"); -let mut s = String::new(); -s.extend(abc.chars()); -s.extend(def.chars()); -``` -The correct use would be: -``` -let abc = "abc"; -let def = String::from("def"); -let mut s = String::new(); -s.push_str(abc); -s.push_str(&def); -``` \ No newline at end of file diff --git a/src/docs/string_from_utf8_as_bytes.txt b/src/docs/string_from_utf8_as_bytes.txt deleted file mode 100644 index 9102d73471cb..000000000000 --- a/src/docs/string_from_utf8_as_bytes.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Check if the string is transformed to byte array and casted back to string. - -### Why is this bad? -It's unnecessary, the string can be used directly. - -### Example -``` -std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap(); -``` - -Use instead: -``` -&"Hello World!"[6..11]; -``` \ No newline at end of file diff --git a/src/docs/string_lit_as_bytes.txt b/src/docs/string_lit_as_bytes.txt deleted file mode 100644 index a125b97ed656..000000000000 --- a/src/docs/string_lit_as_bytes.txt +++ /dev/null @@ -1,39 +0,0 @@ -### What it does -Checks for the `as_bytes` method called on string literals -that contain only ASCII characters. - -### Why is this bad? -Byte string literals (e.g., `b"foo"`) can be used -instead. They are shorter but less discoverable than `as_bytes()`. - -### Known problems -`"str".as_bytes()` and the suggested replacement of `b"str"` are not -equivalent because they have different types. The former is `&[u8]` -while the latter is `&[u8; 3]`. That means in general they will have a -different set of methods and different trait implementations. - -``` -fn f(v: Vec) {} - -f("...".as_bytes().to_owned()); // works -f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec - -fn g(r: impl std::io::Read) {} - -g("...".as_bytes()); // works -g(b"..."); // does not work -``` - -The actual equivalent of `"str".as_bytes()` with the same type is not -`b"str"` but `&b"str"[..]`, which is a great deal of punctuation and not -more readable than a function call. - -### Example -``` -let bstr = "a byte string".as_bytes(); -``` - -Use instead: -``` -let bstr = b"a byte string"; -``` \ No newline at end of file diff --git a/src/docs/string_slice.txt b/src/docs/string_slice.txt deleted file mode 100644 index 3d9e49dd39eb..000000000000 --- a/src/docs/string_slice.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for slice operations on strings - -### Why is this bad? -UTF-8 characters span multiple bytes, and it is easy to inadvertently confuse character -counts and string indices. This may lead to panics, and should warrant some test cases -containing wide UTF-8 characters. This lint is most useful in code that should avoid -panics at all costs. - -### Known problems -Probably lots of false positives. If an index comes from a known valid position (e.g. -obtained via `char_indices` over the same string), it is totally OK. - -# Example -``` -&"Ölkanne"[1..]; -``` \ No newline at end of file diff --git a/src/docs/string_to_string.txt b/src/docs/string_to_string.txt deleted file mode 100644 index deb7eebe7842..000000000000 --- a/src/docs/string_to_string.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -This lint checks for `.to_string()` method calls on values of type `String`. - -### Why is this bad? -The `to_string` method is also used on other types to convert them to a string. -When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`. - -### Example -``` -// example code where clippy issues a warning -let msg = String::from("Hello World"); -let _ = msg.to_string(); -``` -Use instead: -``` -// example code which does not raise clippy warning -let msg = String::from("Hello World"); -let _ = msg.clone(); -``` \ No newline at end of file diff --git a/src/docs/strlen_on_c_strings.txt b/src/docs/strlen_on_c_strings.txt deleted file mode 100644 index 0454abf55a20..000000000000 --- a/src/docs/strlen_on_c_strings.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for usage of `libc::strlen` on a `CString` or `CStr` value, -and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instead. - -### Why is this bad? -This avoids calling an unsafe `libc` function. -Currently, it also avoids calculating the length. - -### Example -``` -use std::ffi::CString; -let cstring = CString::new("foo").expect("CString::new failed"); -let len = unsafe { libc::strlen(cstring.as_ptr()) }; -``` -Use instead: -``` -use std::ffi::CString; -let cstring = CString::new("foo").expect("CString::new failed"); -let len = cstring.as_bytes().len(); -``` \ No newline at end of file diff --git a/src/docs/struct_excessive_bools.txt b/src/docs/struct_excessive_bools.txt deleted file mode 100644 index 9e197c786201..000000000000 --- a/src/docs/struct_excessive_bools.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for excessive -use of bools in structs. - -### Why is this bad? -Excessive bools in a struct -is often a sign that it's used as a state machine, -which is much better implemented as an enum. -If it's not the case, excessive bools usually benefit -from refactoring into two-variant enums for better -readability and API. - -### Example -``` -struct S { - is_pending: bool, - is_processing: bool, - is_finished: bool, -} -``` - -Use instead: -``` -enum S { - Pending, - Processing, - Finished, -} -``` \ No newline at end of file diff --git a/src/docs/suboptimal_flops.txt b/src/docs/suboptimal_flops.txt deleted file mode 100644 index f1c9c665b085..000000000000 --- a/src/docs/suboptimal_flops.txt +++ /dev/null @@ -1,50 +0,0 @@ -### What it does -Looks for floating-point expressions that -can be expressed using built-in methods to improve both -accuracy and performance. - -### Why is this bad? -Negatively impacts accuracy and performance. - -### Example -``` -use std::f32::consts::E; - -let a = 3f32; -let _ = (2f32).powf(a); -let _ = E.powf(a); -let _ = a.powf(1.0 / 2.0); -let _ = a.log(2.0); -let _ = a.log(10.0); -let _ = a.log(E); -let _ = a.powf(2.0); -let _ = a * 2.0 + 4.0; -let _ = if a < 0.0 { - -a -} else { - a -}; -let _ = if a < 0.0 { - a -} else { - -a -}; -``` - -is better expressed as - -``` -use std::f32::consts::E; - -let a = 3f32; -let _ = a.exp2(); -let _ = a.exp(); -let _ = a.sqrt(); -let _ = a.log2(); -let _ = a.log10(); -let _ = a.ln(); -let _ = a.powi(2); -let _ = a.mul_add(2.0, 4.0); -let _ = a.abs(); -let _ = -a.abs(); -``` \ No newline at end of file diff --git a/src/docs/suspicious_arithmetic_impl.txt b/src/docs/suspicious_arithmetic_impl.txt deleted file mode 100644 index d67ff279346d..000000000000 --- a/src/docs/suspicious_arithmetic_impl.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Lints for suspicious operations in impls of arithmetic operators, e.g. -subtracting elements in an Add impl. - -### Why is this bad? -This is probably a typo or copy-and-paste error and not intended. - -### Example -``` -impl Add for Foo { - type Output = Foo; - - fn add(self, other: Foo) -> Foo { - Foo(self.0 - other.0) - } -} -``` \ No newline at end of file diff --git a/src/docs/suspicious_assignment_formatting.txt b/src/docs/suspicious_assignment_formatting.txt deleted file mode 100644 index b889827cdf27..000000000000 --- a/src/docs/suspicious_assignment_formatting.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for use of the non-existent `=*`, `=!` and `=-` -operators. - -### Why is this bad? -This is either a typo of `*=`, `!=` or `-=` or -confusing. - -### Example -``` -a =- 42; // confusing, should it be `a -= 42` or `a = -42`? -``` \ No newline at end of file diff --git a/src/docs/suspicious_else_formatting.txt b/src/docs/suspicious_else_formatting.txt deleted file mode 100644 index 3cf2f74868eb..000000000000 --- a/src/docs/suspicious_else_formatting.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for formatting of `else`. It lints if the `else` -is followed immediately by a newline or the `else` seems to be missing. - -### Why is this bad? -This is probably some refactoring remnant, even if the -code is correct, it might look confusing. - -### Example -``` -if foo { -} { // looks like an `else` is missing here -} - -if foo { -} if bar { // looks like an `else` is missing here -} - -if foo { -} else - -{ // this is the `else` block of the previous `if`, but should it be? -} - -if foo { -} else - -if bar { // this is the `else` block of the previous `if`, but should it be? -} -``` \ No newline at end of file diff --git a/src/docs/suspicious_map.txt b/src/docs/suspicious_map.txt deleted file mode 100644 index d8fa52c43fb0..000000000000 --- a/src/docs/suspicious_map.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for calls to `map` followed by a `count`. - -### Why is this bad? -It looks suspicious. Maybe `map` was confused with `filter`. -If the `map` call is intentional, this should be rewritten -using `inspect`. Or, if you intend to drive the iterator to -completion, you can just use `for_each` instead. - -### Example -``` -let _ = (0..3).map(|x| x + 2).count(); -``` \ No newline at end of file diff --git a/src/docs/suspicious_op_assign_impl.txt b/src/docs/suspicious_op_assign_impl.txt deleted file mode 100644 index 81abfbecae07..000000000000 --- a/src/docs/suspicious_op_assign_impl.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Lints for suspicious operations in impls of OpAssign, e.g. -subtracting elements in an AddAssign impl. - -### Why is this bad? -This is probably a typo or copy-and-paste error and not intended. - -### Example -``` -impl AddAssign for Foo { - fn add_assign(&mut self, other: Foo) { - *self = *self - other; - } -} -``` \ No newline at end of file diff --git a/src/docs/suspicious_operation_groupings.txt b/src/docs/suspicious_operation_groupings.txt deleted file mode 100644 index 81ede5d3da57..000000000000 --- a/src/docs/suspicious_operation_groupings.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Checks for unlikely usages of binary operators that are almost -certainly typos and/or copy/paste errors, given the other usages -of binary operators nearby. - -### Why is this bad? -They are probably bugs and if they aren't then they look like bugs -and you should add a comment explaining why you are doing such an -odd set of operations. - -### Known problems -There may be some false positives if you are trying to do something -unusual that happens to look like a typo. - -### Example -``` -struct Vec3 { - x: f64, - y: f64, - z: f64, -} - -impl Eq for Vec3 {} - -impl PartialEq for Vec3 { - fn eq(&self, other: &Self) -> bool { - // This should trigger the lint because `self.x` is compared to `other.y` - self.x == other.y && self.y == other.y && self.z == other.z - } -} -``` -Use instead: -``` -// same as above except: -impl PartialEq for Vec3 { - fn eq(&self, other: &Self) -> bool { - // Note we now compare other.x to self.x - self.x == other.x && self.y == other.y && self.z == other.z - } -} -``` \ No newline at end of file diff --git a/src/docs/suspicious_splitn.txt b/src/docs/suspicious_splitn.txt deleted file mode 100644 index 79a3dbfa6f4a..000000000000 --- a/src/docs/suspicious_splitn.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for calls to [`splitn`] -(https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and -related functions with either zero or one splits. - -### Why is this bad? -These calls don't actually split the value and are -likely to be intended as a different number. - -### Example -``` -for x in s.splitn(1, ":") { - // .. -} -``` - -Use instead: -``` -for x in s.splitn(2, ":") { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/suspicious_to_owned.txt b/src/docs/suspicious_to_owned.txt deleted file mode 100644 index 8cbf61dc7175..000000000000 --- a/src/docs/suspicious_to_owned.txt +++ /dev/null @@ -1,39 +0,0 @@ -### What it does -Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`. - -### Why is this bad? -Calling `to_owned()` on a `Cow` creates a clone of the `Cow` -itself, without taking ownership of the `Cow` contents (i.e. -it's equivalent to calling `Cow::clone`). -The similarly named `into_owned` method, on the other hand, -clones the `Cow` contents, effectively turning any `Cow::Borrowed` -into a `Cow::Owned`. - -Given the potential ambiguity, consider replacing `to_owned` -with `clone` for better readability or, if getting a `Cow::Owned` -was the original intent, using `into_owned` instead. - -### Example -``` -let s = "Hello world!"; -let cow = Cow::Borrowed(s); - -let data = cow.to_owned(); -assert!(matches!(data, Cow::Borrowed(_))) -``` -Use instead: -``` -let s = "Hello world!"; -let cow = Cow::Borrowed(s); - -let data = cow.clone(); -assert!(matches!(data, Cow::Borrowed(_))) -``` -or -``` -let s = "Hello world!"; -let cow = Cow::Borrowed(s); - -let data = cow.into_owned(); -assert!(matches!(data, String)) -``` \ No newline at end of file diff --git a/src/docs/suspicious_unary_op_formatting.txt b/src/docs/suspicious_unary_op_formatting.txt deleted file mode 100644 index 06fb09db76d0..000000000000 --- a/src/docs/suspicious_unary_op_formatting.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks the formatting of a unary operator on the right hand side -of a binary operator. It lints if there is no space between the binary and unary operators, -but there is a space between the unary and its operand. - -### Why is this bad? -This is either a typo in the binary operator or confusing. - -### Example -``` -// &&! looks like a different operator -if foo &&! bar {} -``` - -Use instead: -``` -if foo && !bar {} -``` \ No newline at end of file diff --git a/src/docs/swap_ptr_to_ref.txt b/src/docs/swap_ptr_to_ref.txt deleted file mode 100644 index 0215d1e8a421..000000000000 --- a/src/docs/swap_ptr_to_ref.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for calls to `core::mem::swap` where either parameter is derived from a pointer - -### Why is this bad? -When at least one parameter to `swap` is derived from a pointer it may overlap with the -other. This would then lead to undefined behavior. - -### Example -``` -unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) { - for (&x, &y) in x.iter().zip(y) { - core::mem::swap(&mut *x, &mut *y); - } -} -``` -Use instead: -``` -unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) { - for (&x, &y) in x.iter().zip(y) { - core::ptr::swap(x, y); - } -} -``` \ No newline at end of file diff --git a/src/docs/tabs_in_doc_comments.txt b/src/docs/tabs_in_doc_comments.txt deleted file mode 100644 index f83dbe2b73cb..000000000000 --- a/src/docs/tabs_in_doc_comments.txt +++ /dev/null @@ -1,44 +0,0 @@ -### What it does -Checks doc comments for usage of tab characters. - -### Why is this bad? -The rust style-guide promotes spaces instead of tabs for indentation. -To keep a consistent view on the source, also doc comments should not have tabs. -Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the -display settings of the author and reader differ. - -### Example -``` -/// -/// Struct to hold two strings: -/// - first one -/// - second one -pub struct DoubleString { - /// - /// - First String: - /// - needs to be inside here - first_string: String, - /// - /// - Second String: - /// - needs to be inside here - second_string: String, -} -``` - -Will be converted to: -``` -/// -/// Struct to hold two strings: -/// - first one -/// - second one -pub struct DoubleString { - /// - /// - First String: - /// - needs to be inside here - first_string: String, - /// - /// - Second String: - /// - needs to be inside here - second_string: String, -} -``` \ No newline at end of file diff --git a/src/docs/temporary_assignment.txt b/src/docs/temporary_assignment.txt deleted file mode 100644 index 195b42cf0d49..000000000000 --- a/src/docs/temporary_assignment.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for construction of a structure or tuple just to -assign a value in it. - -### Why is this bad? -Readability. If the structure is only created to be -updated, why not write the structure you want in the first place? - -### Example -``` -(0, 0).0 = 1 -``` \ No newline at end of file diff --git a/src/docs/to_digit_is_some.txt b/src/docs/to_digit_is_some.txt deleted file mode 100644 index eee8375adf7a..000000000000 --- a/src/docs/to_digit_is_some.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for `.to_digit(..).is_some()` on `char`s. - -### Why is this bad? -This is a convoluted way of checking if a `char` is a digit. It's -more straight forward to use the dedicated `is_digit` method. - -### Example -``` -let is_digit = c.to_digit(radix).is_some(); -``` -can be written as: -``` -let is_digit = c.is_digit(radix); -``` \ No newline at end of file diff --git a/src/docs/to_string_in_format_args.txt b/src/docs/to_string_in_format_args.txt deleted file mode 100644 index 34b20583585a..000000000000 --- a/src/docs/to_string_in_format_args.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string) -applied to a type that implements [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html) -in a macro that does formatting. - -### Why is this bad? -Since the type implements `Display`, the use of `to_string` is -unnecessary. - -### Example -``` -println!("error: something failed at {}", Location::caller().to_string()); -``` -Use instead: -``` -println!("error: something failed at {}", Location::caller()); -``` \ No newline at end of file diff --git a/src/docs/todo.txt b/src/docs/todo.txt deleted file mode 100644 index 661eb1ac5cff..000000000000 --- a/src/docs/todo.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for usage of `todo!`. - -### Why is this bad? -This macro should not be present in production code - -### Example -``` -todo!(); -``` \ No newline at end of file diff --git a/src/docs/too_many_arguments.txt b/src/docs/too_many_arguments.txt deleted file mode 100644 index 4669f9f82e66..000000000000 --- a/src/docs/too_many_arguments.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for functions with too many parameters. - -### Why is this bad? -Functions with lots of parameters are considered bad -style and reduce readability (“what does the 5th parameter mean?”). Consider -grouping some parameters into a new type. - -### Example -``` -fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/too_many_lines.txt b/src/docs/too_many_lines.txt deleted file mode 100644 index 425db348bbd7..000000000000 --- a/src/docs/too_many_lines.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for functions with a large amount of lines. - -### Why is this bad? -Functions with a lot of lines are harder to understand -due to having to look at a larger amount of code to understand what the -function is doing. Consider splitting the body of the function into -multiple functions. - -### Example -``` -fn im_too_long() { - println!(""); - // ... 100 more LoC - println!(""); -} -``` \ No newline at end of file diff --git a/src/docs/toplevel_ref_arg.txt b/src/docs/toplevel_ref_arg.txt deleted file mode 100644 index 96a9e2db8b7e..000000000000 --- a/src/docs/toplevel_ref_arg.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for function arguments and let bindings denoted as -`ref`. - -### Why is this bad? -The `ref` declaration makes the function take an owned -value, but turns the argument into a reference (which means that the value -is destroyed when exiting the function). This adds not much value: either -take a reference type, or take an owned value and create references in the -body. - -For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The -type of `x` is more obvious with the former. - -### Known problems -If the argument is dereferenced within the function, -removing the `ref` will lead to errors. This can be fixed by removing the -dereferences, e.g., changing `*x` to `x` within the function. - -### Example -``` -fn foo(ref _x: u8) {} -``` - -Use instead: -``` -fn foo(_x: &u8) {} -``` \ No newline at end of file diff --git a/src/docs/trailing_empty_array.txt b/src/docs/trailing_empty_array.txt deleted file mode 100644 index db1908cc96d0..000000000000 --- a/src/docs/trailing_empty_array.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Displays a warning when a struct with a trailing zero-sized array is declared without a `repr` attribute. - -### Why is this bad? -Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code or in some other situation where control over memory layout matters (for example, in conjunction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` (or another `repr` attribute) is needed. - -### Example -``` -struct RarelyUseful { - some_field: u32, - last: [u32; 0], -} -``` - -Use instead: -``` -#[repr(C)] -struct MoreOftenUseful { - some_field: usize, - last: [u32; 0], -} -``` \ No newline at end of file diff --git a/src/docs/trait_duplication_in_bounds.txt b/src/docs/trait_duplication_in_bounds.txt deleted file mode 100644 index 509736bb3644..000000000000 --- a/src/docs/trait_duplication_in_bounds.txt +++ /dev/null @@ -1,37 +0,0 @@ -### What it does -Checks for cases where generics are being used and multiple -syntax specifications for trait bounds are used simultaneously. - -### Why is this bad? -Duplicate bounds makes the code -less readable than specifying them only once. - -### Example -``` -fn func(arg: T) where T: Clone + Default {} -``` - -Use instead: -``` -fn func(arg: T) {} - -// or - -fn func(arg: T) where T: Clone + Default {} -``` - -``` -fn foo(bar: T) {} -``` -Use instead: -``` -fn foo(bar: T) {} -``` - -``` -fn foo(bar: T) where T: Default + Default {} -``` -Use instead: -``` -fn foo(bar: T) where T: Default {} -``` \ No newline at end of file diff --git a/src/docs/transmute_bytes_to_str.txt b/src/docs/transmute_bytes_to_str.txt deleted file mode 100644 index 75889b9c73ae..000000000000 --- a/src/docs/transmute_bytes_to_str.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for transmutes from a `&[u8]` to a `&str`. - -### Why is this bad? -Not every byte slice is a valid UTF-8 string. - -### Known problems -- [`from_utf8`] which this lint suggests using is slower than `transmute` -as it needs to validate the input. -If you are certain that the input is always a valid UTF-8, -use [`from_utf8_unchecked`] which is as fast as `transmute` -but has a semantically meaningful name. -- You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`. - -[`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html -[`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html - -### Example -``` -let b: &[u8] = &[1_u8, 2_u8]; -unsafe { - let _: &str = std::mem::transmute(b); // where b: &[u8] -} - -// should be: -let _ = std::str::from_utf8(b).unwrap(); -``` \ No newline at end of file diff --git a/src/docs/transmute_float_to_int.txt b/src/docs/transmute_float_to_int.txt deleted file mode 100644 index 1877e5a465a4..000000000000 --- a/src/docs/transmute_float_to_int.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for transmutes from a float to an integer. - -### Why is this bad? -Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive -and safe. - -### Example -``` -unsafe { - let _: u32 = std::mem::transmute(1f32); -} - -// should be: -let _: u32 = 1f32.to_bits(); -``` \ No newline at end of file diff --git a/src/docs/transmute_int_to_bool.txt b/src/docs/transmute_int_to_bool.txt deleted file mode 100644 index 07c10f8d0bca..000000000000 --- a/src/docs/transmute_int_to_bool.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for transmutes from an integer to a `bool`. - -### Why is this bad? -This might result in an invalid in-memory representation of a `bool`. - -### Example -``` -let x = 1_u8; -unsafe { - let _: bool = std::mem::transmute(x); // where x: u8 -} - -// should be: -let _: bool = x != 0; -``` \ No newline at end of file diff --git a/src/docs/transmute_int_to_char.txt b/src/docs/transmute_int_to_char.txt deleted file mode 100644 index 836d22d3f99c..000000000000 --- a/src/docs/transmute_int_to_char.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for transmutes from an integer to a `char`. - -### Why is this bad? -Not every integer is a Unicode scalar value. - -### Known problems -- [`from_u32`] which this lint suggests using is slower than `transmute` -as it needs to validate the input. -If you are certain that the input is always a valid Unicode scalar value, -use [`from_u32_unchecked`] which is as fast as `transmute` -but has a semantically meaningful name. -- You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`. - -[`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html -[`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html - -### Example -``` -let x = 1_u32; -unsafe { - let _: char = std::mem::transmute(x); // where x: u32 -} - -// should be: -let _ = std::char::from_u32(x).unwrap(); -``` \ No newline at end of file diff --git a/src/docs/transmute_int_to_float.txt b/src/docs/transmute_int_to_float.txt deleted file mode 100644 index 75cdc62e9727..000000000000 --- a/src/docs/transmute_int_to_float.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for transmutes from an integer to a float. - -### Why is this bad? -Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive -and safe. - -### Example -``` -unsafe { - let _: f32 = std::mem::transmute(1_u32); // where x: u32 -} - -// should be: -let _: f32 = f32::from_bits(1_u32); -``` \ No newline at end of file diff --git a/src/docs/transmute_num_to_bytes.txt b/src/docs/transmute_num_to_bytes.txt deleted file mode 100644 index a2c39a1b947e..000000000000 --- a/src/docs/transmute_num_to_bytes.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for transmutes from a number to an array of `u8` - -### Why this is bad? -Transmutes are dangerous and error-prone, whereas `to_ne_bytes` -is intuitive and safe. - -### Example -``` -unsafe { - let x: [u8; 8] = std::mem::transmute(1i64); -} - -// should be -let x: [u8; 8] = 0i64.to_ne_bytes(); -``` \ No newline at end of file diff --git a/src/docs/transmute_ptr_to_ptr.txt b/src/docs/transmute_ptr_to_ptr.txt deleted file mode 100644 index 65777db98618..000000000000 --- a/src/docs/transmute_ptr_to_ptr.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for transmutes from a pointer to a pointer, or -from a reference to a reference. - -### Why is this bad? -Transmutes are dangerous, and these can instead be -written as casts. - -### Example -``` -let ptr = &1u32 as *const u32; -unsafe { - // pointer-to-pointer transmute - let _: *const f32 = std::mem::transmute(ptr); - // ref-ref transmute - let _: &f32 = std::mem::transmute(&1u32); -} -// These can be respectively written: -let _ = ptr as *const f32; -let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) }; -``` \ No newline at end of file diff --git a/src/docs/transmute_ptr_to_ref.txt b/src/docs/transmute_ptr_to_ref.txt deleted file mode 100644 index aca550f5036e..000000000000 --- a/src/docs/transmute_ptr_to_ref.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for transmutes from a pointer to a reference. - -### Why is this bad? -This can always be rewritten with `&` and `*`. - -### Known problems -- `mem::transmute` in statics and constants is stable from Rust 1.46.0, -while dereferencing raw pointer is not stable yet. -If you need to do this in those places, -you would have to use `transmute` instead. - -### Example -``` -unsafe { - let _: &T = std::mem::transmute(p); // where p: *const T -} - -// can be written: -let _: &T = &*p; -``` \ No newline at end of file diff --git a/src/docs/transmute_undefined_repr.txt b/src/docs/transmute_undefined_repr.txt deleted file mode 100644 index 5ee6aaf4ca92..000000000000 --- a/src/docs/transmute_undefined_repr.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for transmutes between types which do not have a representation defined relative to -each other. - -### Why is this bad? -The results of such a transmute are not defined. - -### Known problems -This lint has had multiple problems in the past and was moved to `nursery`. See issue -[#8496](https://github.com/rust-lang/rust-clippy/issues/8496) for more details. - -### Example -``` -struct Foo(u32, T); -let _ = unsafe { core::mem::transmute::, Foo>(Foo(0u32, 0u32)) }; -``` -Use instead: -``` -#[repr(C)] -struct Foo(u32, T); -let _ = unsafe { core::mem::transmute::, Foo>(Foo(0u32, 0u32)) }; -``` \ No newline at end of file diff --git a/src/docs/transmutes_expressible_as_ptr_casts.txt b/src/docs/transmutes_expressible_as_ptr_casts.txt deleted file mode 100644 index b68a8cda9c74..000000000000 --- a/src/docs/transmutes_expressible_as_ptr_casts.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for transmutes that could be a pointer cast. - -### Why is this bad? -Readability. The code tricks people into thinking that -something complex is going on. - -### Example - -``` -unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) }; -``` -Use instead: -``` -p as *const [u16]; -``` \ No newline at end of file diff --git a/src/docs/transmuting_null.txt b/src/docs/transmuting_null.txt deleted file mode 100644 index f8bacfc0b90b..000000000000 --- a/src/docs/transmuting_null.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for transmute calls which would receive a null pointer. - -### Why is this bad? -Transmuting a null pointer is undefined behavior. - -### Known problems -Not all cases can be detected at the moment of this writing. -For example, variables which hold a null pointer and are then fed to a `transmute` -call, aren't detectable yet. - -### Example -``` -let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) }; -``` \ No newline at end of file diff --git a/src/docs/trim_split_whitespace.txt b/src/docs/trim_split_whitespace.txt deleted file mode 100644 index f7e3e7858f94..000000000000 --- a/src/docs/trim_split_whitespace.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Warns about calling `str::trim` (or variants) before `str::split_whitespace`. - -### Why is this bad? -`split_whitespace` already ignores leading and trailing whitespace. - -### Example -``` -" A B C ".trim().split_whitespace(); -``` -Use instead: -``` -" A B C ".split_whitespace(); -``` \ No newline at end of file diff --git a/src/docs/trivial_regex.txt b/src/docs/trivial_regex.txt deleted file mode 100644 index f71d667fd771..000000000000 --- a/src/docs/trivial_regex.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for trivial [regex](https://crates.io/crates/regex) -creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`). - -### Why is this bad? -Matching the regex can likely be replaced by `==` or -`str::starts_with`, `str::ends_with` or `std::contains` or other `str` -methods. - -### Known problems -If the same regex is going to be applied to multiple -inputs, the precomputations done by `Regex` construction can give -significantly better performance than any of the `str`-based methods. - -### Example -``` -Regex::new("^foobar") -``` \ No newline at end of file diff --git a/src/docs/trivially_copy_pass_by_ref.txt b/src/docs/trivially_copy_pass_by_ref.txt deleted file mode 100644 index f54cce5e2bd7..000000000000 --- a/src/docs/trivially_copy_pass_by_ref.txt +++ /dev/null @@ -1,43 +0,0 @@ -### What it does -Checks for functions taking arguments by reference, where -the argument type is `Copy` and small enough to be more efficient to always -pass by value. - -### Why is this bad? -In many calling conventions instances of structs will -be passed through registers if they fit into two or less general purpose -registers. - -### Known problems -This lint is target register size dependent, it is -limited to 32-bit to try and reduce portability problems between 32 and -64-bit, but if you are compiling for 8 or 16-bit targets then the limit -will be different. - -The configuration option `trivial_copy_size_limit` can be set to override -this limit for a project. - -This lint attempts to allow passing arguments by reference if a reference -to that argument is returned. This is implemented by comparing the lifetime -of the argument and return value for equality. However, this can cause -false positives in cases involving multiple lifetimes that are bounded by -each other. - -Also, it does not take account of other similar cases where getting memory addresses -matters; namely, returning the pointer to the argument in question, -and passing the argument, as both references and pointers, -to a function that needs the memory address. For further details, refer to -[this issue](https://github.com/rust-lang/rust-clippy/issues/5953) -that explains a real case in which this false positive -led to an **undefined behavior** introduced with unsafe code. - -### Example - -``` -fn foo(v: &u32) {} -``` - -Use instead: -``` -fn foo(v: u32) {} -``` \ No newline at end of file diff --git a/src/docs/try_err.txt b/src/docs/try_err.txt deleted file mode 100644 index e3d4ef3a09df..000000000000 --- a/src/docs/try_err.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for usages of `Err(x)?`. - -### Why is this bad? -The `?` operator is designed to allow calls that -can fail to be easily chained. For example, `foo()?.bar()` or -`foo(bar()?)`. Because `Err(x)?` can't be used that way (it will -always return), it is more clear to write `return Err(x)`. - -### Example -``` -fn foo(fail: bool) -> Result { - if fail { - Err("failed")?; - } - Ok(0) -} -``` -Could be written: - -``` -fn foo(fail: bool) -> Result { - if fail { - return Err("failed".into()); - } - Ok(0) -} -``` \ No newline at end of file diff --git a/src/docs/type_complexity.txt b/src/docs/type_complexity.txt deleted file mode 100644 index 69cd87500504..000000000000 --- a/src/docs/type_complexity.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for types used in structs, parameters and `let` -declarations above a certain complexity threshold. - -### Why is this bad? -Too complex types make the code less readable. Consider -using a `type` definition to simplify them. - -### Example -``` -struct Foo { - inner: Rc>>>, -} -``` \ No newline at end of file diff --git a/src/docs/type_repetition_in_bounds.txt b/src/docs/type_repetition_in_bounds.txt deleted file mode 100644 index 18ed372fd13e..000000000000 --- a/src/docs/type_repetition_in_bounds.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns about unnecessary type repetitions in trait bounds - -### Why is this bad? -Repeating the type for every bound makes the code -less readable than combining the bounds - -### Example -``` -pub fn foo(t: T) where T: Copy, T: Clone {} -``` - -Use instead: -``` -pub fn foo(t: T) where T: Copy + Clone {} -``` \ No newline at end of file diff --git a/src/docs/undocumented_unsafe_blocks.txt b/src/docs/undocumented_unsafe_blocks.txt deleted file mode 100644 index f3af4753c5f7..000000000000 --- a/src/docs/undocumented_unsafe_blocks.txt +++ /dev/null @@ -1,43 +0,0 @@ -### What it does -Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment -explaining why the unsafe operations performed inside -the block are safe. - -Note the comment must appear on the line(s) preceding the unsafe block -with nothing appearing in between. The following is ok: -``` -foo( - // SAFETY: - // This is a valid safety comment - unsafe { *x } -) -``` -But neither of these are: -``` -// SAFETY: -// This is not a valid safety comment -foo( - /* SAFETY: Neither is this */ unsafe { *x }, -); -``` - -### Why is this bad? -Undocumented unsafe blocks and impls can make it difficult to -read and maintain code, as well as uncover unsoundness -and bugs. - -### Example -``` -use std::ptr::NonNull; -let a = &mut 42; - -let ptr = unsafe { NonNull::new_unchecked(a) }; -``` -Use instead: -``` -use std::ptr::NonNull; -let a = &mut 42; - -// SAFETY: references are guaranteed to be non-null. -let ptr = unsafe { NonNull::new_unchecked(a) }; -``` \ No newline at end of file diff --git a/src/docs/undropped_manually_drops.txt b/src/docs/undropped_manually_drops.txt deleted file mode 100644 index 85e3ec56653c..000000000000 --- a/src/docs/undropped_manually_drops.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. - -### Why is this bad? -The safe `drop` function does not drop the inner value of a `ManuallyDrop`. - -### Known problems -Does not catch cases if the user binds `std::mem::drop` -to a different name and calls it that way. - -### Example -``` -struct S; -drop(std::mem::ManuallyDrop::new(S)); -``` -Use instead: -``` -struct S; -unsafe { - std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); -} -``` \ No newline at end of file diff --git a/src/docs/unicode_not_nfc.txt b/src/docs/unicode_not_nfc.txt deleted file mode 100644 index c660c51dadb2..000000000000 --- a/src/docs/unicode_not_nfc.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for string literals that contain Unicode in a form -that is not equal to its -[NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms). - -### Why is this bad? -If such a string is compared to another, the results -may be surprising. - -### Example -You may not see it, but "à"" and "à"" aren't the same string. The -former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`. \ No newline at end of file diff --git a/src/docs/unimplemented.txt b/src/docs/unimplemented.txt deleted file mode 100644 index 7095594fb2e7..000000000000 --- a/src/docs/unimplemented.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for usage of `unimplemented!`. - -### Why is this bad? -This macro should not be present in production code - -### Example -``` -unimplemented!(); -``` \ No newline at end of file diff --git a/src/docs/uninit_assumed_init.txt b/src/docs/uninit_assumed_init.txt deleted file mode 100644 index cca24093d40d..000000000000 --- a/src/docs/uninit_assumed_init.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for `MaybeUninit::uninit().assume_init()`. - -### Why is this bad? -For most types, this is undefined behavior. - -### Known problems -For now, we accept empty tuples and tuples / arrays -of `MaybeUninit`. There may be other types that allow uninitialized -data, but those are not yet rigorously defined. - -### Example -``` -// Beware the UB -use std::mem::MaybeUninit; - -let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; -``` - -Note that the following is OK: - -``` -use std::mem::MaybeUninit; - -let _: [MaybeUninit; 5] = unsafe { - MaybeUninit::uninit().assume_init() -}; -``` \ No newline at end of file diff --git a/src/docs/uninit_vec.txt b/src/docs/uninit_vec.txt deleted file mode 100644 index cd50afe78f6f..000000000000 --- a/src/docs/uninit_vec.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Checks for `set_len()` call that creates `Vec` with uninitialized elements. -This is commonly caused by calling `set_len()` right after allocating or -reserving a buffer with `new()`, `default()`, `with_capacity()`, or `reserve()`. - -### Why is this bad? -It creates a `Vec` with uninitialized data, which leads to -undefined behavior with most safe operations. Notably, uninitialized -`Vec` must not be used with generic `Read`. - -Moreover, calling `set_len()` on a `Vec` created with `new()` or `default()` -creates out-of-bound values that lead to heap memory corruption when used. - -### Known Problems -This lint only checks directly adjacent statements. - -### Example -``` -let mut vec: Vec = Vec::with_capacity(1000); -unsafe { vec.set_len(1000); } -reader.read(&mut vec); // undefined behavior! -``` - -### How to fix? -1. Use an initialized buffer: - ```rust,ignore - let mut vec: Vec = vec![0; 1000]; - reader.read(&mut vec); - ``` -2. Wrap the content in `MaybeUninit`: - ```rust,ignore - let mut vec: Vec> = Vec::with_capacity(1000); - vec.set_len(1000); // `MaybeUninit` can be uninitialized - ``` -3. If you are on 1.60.0 or later, `Vec::spare_capacity_mut()` is available: - ```rust,ignore - let mut vec: Vec = Vec::with_capacity(1000); - let remaining = vec.spare_capacity_mut(); // `&mut [MaybeUninit]` - // perform initialization with `remaining` - vec.set_len(...); // Safe to call `set_len()` on initialized part - ``` \ No newline at end of file diff --git a/src/docs/uninlined_format_args.txt b/src/docs/uninlined_format_args.txt deleted file mode 100644 index 3d2966c84dbe..000000000000 --- a/src/docs/uninlined_format_args.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Detect when a variable is not inlined in a format string, -and suggests to inline it. - -### Why is this bad? -Non-inlined code is slightly more difficult to read and understand, -as it requires arguments to be matched against the format string. -The inlined syntax, where allowed, is simpler. - -### Example -``` -format!("{}", var); -format!("{v:?}", v = var); -format!("{0} {0}", var); -format!("{0:1$}", var, width); -format!("{:.*}", prec, var); -``` -Use instead: -``` -format!("{var}"); -format!("{var:?}"); -format!("{var} {var}"); -format!("{var:width$}"); -format!("{var:.prec$}"); -``` - -### Known Problems - -There may be a false positive if the format string is expanded from certain proc macros: - -``` -println!(indoc!("{}"), var); -``` - -If a format string contains a numbered argument that cannot be inlined -nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. \ No newline at end of file diff --git a/src/docs/unit_arg.txt b/src/docs/unit_arg.txt deleted file mode 100644 index eb83403bb275..000000000000 --- a/src/docs/unit_arg.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for passing a unit value as an argument to a function without using a -unit literal (`()`). - -### Why is this bad? -This is likely the result of an accidental semicolon. - -### Example -``` -foo({ - let a = bar(); - baz(a); -}) -``` \ No newline at end of file diff --git a/src/docs/unit_cmp.txt b/src/docs/unit_cmp.txt deleted file mode 100644 index 6f3d62010dc5..000000000000 --- a/src/docs/unit_cmp.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks for comparisons to unit. This includes all binary -comparisons (like `==` and `<`) and asserts. - -### Why is this bad? -Unit is always equal to itself, and thus is just a -clumsily written constant. Mostly this happens when someone accidentally -adds semicolons at the end of the operands. - -### Example -``` -if { - foo(); -} == { - bar(); -} { - baz(); -} -``` -is equal to -``` -{ - foo(); - bar(); - baz(); -} -``` - -For asserts: -``` -assert_eq!({ foo(); }, { bar(); }); -``` -will always succeed \ No newline at end of file diff --git a/src/docs/unit_hash.txt b/src/docs/unit_hash.txt deleted file mode 100644 index a22d2994602a..000000000000 --- a/src/docs/unit_hash.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Detects `().hash(_)`. - -### Why is this bad? -Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op. - -### Example -``` -match my_enum { - Empty => ().hash(&mut state), - WithValue(x) => x.hash(&mut state), -} -``` -Use instead: -``` -match my_enum { - Empty => 0_u8.hash(&mut state), - WithValue(x) => x.hash(&mut state), -} -``` \ No newline at end of file diff --git a/src/docs/unit_return_expecting_ord.txt b/src/docs/unit_return_expecting_ord.txt deleted file mode 100644 index 781feac5afcf..000000000000 --- a/src/docs/unit_return_expecting_ord.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for functions that expect closures of type -Fn(...) -> Ord where the implemented closure returns the unit type. -The lint also suggests to remove the semi-colon at the end of the statement if present. - -### Why is this bad? -Likely, returning the unit type is unintentional, and -could simply be caused by an extra semi-colon. Since () implements Ord -it doesn't cause a compilation error. -This is the same reasoning behind the unit_cmp lint. - -### Known problems -If returning unit is intentional, then there is no -way of specifying this without triggering needless_return lint - -### Example -``` -let mut twins = vec!((1, 1), (2, 2)); -twins.sort_by_key(|x| { x.1; }); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_cast.txt b/src/docs/unnecessary_cast.txt deleted file mode 100644 index 603f2606099c..000000000000 --- a/src/docs/unnecessary_cast.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for casts to the same type, casts of int literals to integer types -and casts of float literals to float types. - -### Why is this bad? -It's just unnecessary. - -### Example -``` -let _ = 2i32 as i32; -let _ = 0.5 as f32; -``` - -Better: - -``` -let _ = 2_i32; -let _ = 0.5_f32; -``` \ No newline at end of file diff --git a/src/docs/unnecessary_filter_map.txt b/src/docs/unnecessary_filter_map.txt deleted file mode 100644 index b19341ecf660..000000000000 --- a/src/docs/unnecessary_filter_map.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for `filter_map` calls that could be replaced by `filter` or `map`. -More specifically it checks if the closure provided is only performing one of the -filter or map operations and suggests the appropriate option. - -### Why is this bad? -Complexity. The intent is also clearer if only a single -operation is being performed. - -### Example -``` -let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None }); - -// As there is no transformation of the argument this could be written as: -let _ = (0..3).filter(|&x| x > 2); -``` - -``` -let _ = (0..4).filter_map(|x| Some(x + 1)); - -// As there is no conditional check on the argument this could be written as: -let _ = (0..4).map(|x| x + 1); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_find_map.txt b/src/docs/unnecessary_find_map.txt deleted file mode 100644 index f9444dc48ad6..000000000000 --- a/src/docs/unnecessary_find_map.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for `find_map` calls that could be replaced by `find` or `map`. More -specifically it checks if the closure provided is only performing one of the -find or map operations and suggests the appropriate option. - -### Why is this bad? -Complexity. The intent is also clearer if only a single -operation is being performed. - -### Example -``` -let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None }); - -// As there is no transformation of the argument this could be written as: -let _ = (0..3).find(|&x| x > 2); -``` - -``` -let _ = (0..4).find_map(|x| Some(x + 1)); - -// As there is no conditional check on the argument this could be written as: -let _ = (0..4).map(|x| x + 1).next(); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_fold.txt b/src/docs/unnecessary_fold.txt deleted file mode 100644 index e1b0e65f5197..000000000000 --- a/src/docs/unnecessary_fold.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for using `fold` when a more succinct alternative exists. -Specifically, this checks for `fold`s which could be replaced by `any`, `all`, -`sum` or `product`. - -### Why is this bad? -Readability. - -### Example -``` -(0..3).fold(false, |acc, x| acc || x > 2); -``` - -Use instead: -``` -(0..3).any(|x| x > 2); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_join.txt b/src/docs/unnecessary_join.txt deleted file mode 100644 index ee4e78601f84..000000000000 --- a/src/docs/unnecessary_join.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for use of `.collect::>().join("")` on iterators. - -### Why is this bad? -`.collect::()` is more concise and might be more performant - -### Example -``` -let vector = vec!["hello", "world"]; -let output = vector.iter().map(|item| item.to_uppercase()).collect::>().join(""); -println!("{}", output); -``` -The correct use would be: -``` -let vector = vec!["hello", "world"]; -let output = vector.iter().map(|item| item.to_uppercase()).collect::(); -println!("{}", output); -``` -### Known problems -While `.collect::()` is sometimes more performant, there are cases where -using `.collect::()` over `.collect::>().join("")` -will prevent loop unrolling and will result in a negative performance impact. - -Additionally, differences have been observed between aarch64 and x86_64 assembly output, -with aarch64 tending to producing faster assembly in more cases when using `.collect::()` \ No newline at end of file diff --git a/src/docs/unnecessary_lazy_evaluations.txt b/src/docs/unnecessary_lazy_evaluations.txt deleted file mode 100644 index 208188ce971a..000000000000 --- a/src/docs/unnecessary_lazy_evaluations.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -As the counterpart to `or_fun_call`, this lint looks for unnecessary -lazily evaluated closures on `Option` and `Result`. - -This lint suggests changing the following functions, when eager evaluation results in -simpler code: - - `unwrap_or_else` to `unwrap_or` - - `and_then` to `and` - - `or_else` to `or` - - `get_or_insert_with` to `get_or_insert` - - `ok_or_else` to `ok_or` - -### Why is this bad? -Using eager evaluation is shorter and simpler in some cases. - -### Known problems -It is possible, but not recommended for `Deref` and `Index` to have -side effects. Eagerly evaluating them can change the semantics of the program. - -### Example -``` -// example code where clippy issues a warning -let opt: Option = None; - -opt.unwrap_or_else(|| 42); -``` -Use instead: -``` -let opt: Option = None; - -opt.unwrap_or(42); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_mut_passed.txt b/src/docs/unnecessary_mut_passed.txt deleted file mode 100644 index 2f8bdd113dfd..000000000000 --- a/src/docs/unnecessary_mut_passed.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Detects passing a mutable reference to a function that only -requires an immutable reference. - -### Why is this bad? -The mutable reference rules out all other references to -the value. Also the code misleads about the intent of the call site. - -### Example -``` -vec.push(&mut value); -``` - -Use instead: -``` -vec.push(&value); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_operation.txt b/src/docs/unnecessary_operation.txt deleted file mode 100644 index 7f455e264cb3..000000000000 --- a/src/docs/unnecessary_operation.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for expression statements that can be reduced to a -sub-expression. - -### Why is this bad? -Expressions by themselves often have no side-effects. -Having such expressions reduces readability. - -### Example -``` -compute_array()[0]; -``` \ No newline at end of file diff --git a/src/docs/unnecessary_owned_empty_strings.txt b/src/docs/unnecessary_owned_empty_strings.txt deleted file mode 100644 index 8cd9fba603e0..000000000000 --- a/src/docs/unnecessary_owned_empty_strings.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does - -Detects cases of owned empty strings being passed as an argument to a function expecting `&str` - -### Why is this bad? - -This results in longer and less readable code - -### Example -``` -vec!["1", "2", "3"].join(&String::new()); -``` -Use instead: -``` -vec!["1", "2", "3"].join(""); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_self_imports.txt b/src/docs/unnecessary_self_imports.txt deleted file mode 100644 index b909cd5a76da..000000000000 --- a/src/docs/unnecessary_self_imports.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for imports ending in `::{self}`. - -### Why is this bad? -In most cases, this can be written much more cleanly by omitting `::{self}`. - -### Known problems -Removing `::{self}` will cause any non-module items at the same path to also be imported. -This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt -to detect this scenario and that is why it is a restriction lint. - -### Example -``` -use std::io::{self}; -``` -Use instead: -``` -use std::io; -``` \ No newline at end of file diff --git a/src/docs/unnecessary_sort_by.txt b/src/docs/unnecessary_sort_by.txt deleted file mode 100644 index 6913b62c48eb..000000000000 --- a/src/docs/unnecessary_sort_by.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Detects uses of `Vec::sort_by` passing in a closure -which compares the two arguments, either directly or indirectly. - -### Why is this bad? -It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if -possible) than to use `Vec::sort_by` and a more complicated -closure. - -### Known problems -If the suggested `Vec::sort_by_key` uses Reverse and it isn't already -imported by a use statement, then it will need to be added manually. - -### Example -``` -vec.sort_by(|a, b| a.foo().cmp(&b.foo())); -``` -Use instead: -``` -vec.sort_by_key(|a| a.foo()); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_to_owned.txt b/src/docs/unnecessary_to_owned.txt deleted file mode 100644 index 5d4213bdaf89..000000000000 --- a/src/docs/unnecessary_to_owned.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned) -and other `to_owned`-like functions. - -### Why is this bad? -The unnecessary calls result in useless allocations. - -### Known problems -`unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an -owned copy of a resource and the resource is later used mutably. See -[#8148](https://github.com/rust-lang/rust-clippy/issues/8148). - -### Example -``` -let path = std::path::Path::new("x"); -foo(&path.to_string_lossy().to_string()); -fn foo(s: &str) {} -``` -Use instead: -``` -let path = std::path::Path::new("x"); -foo(&path.to_string_lossy()); -fn foo(s: &str) {} -``` \ No newline at end of file diff --git a/src/docs/unnecessary_unwrap.txt b/src/docs/unnecessary_unwrap.txt deleted file mode 100644 index 50ae845bb44f..000000000000 --- a/src/docs/unnecessary_unwrap.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for calls of `unwrap[_err]()` that cannot fail. - -### Why is this bad? -Using `if let` or `match` is more idiomatic. - -### Example -``` -if option.is_some() { - do_something_with(option.unwrap()) -} -``` - -Could be written: - -``` -if let Some(value) = option { - do_something_with(value) -} -``` \ No newline at end of file diff --git a/src/docs/unnecessary_wraps.txt b/src/docs/unnecessary_wraps.txt deleted file mode 100644 index c0a23d492889..000000000000 --- a/src/docs/unnecessary_wraps.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks for private functions that only return `Ok` or `Some`. - -### Why is this bad? -It is not meaningful to wrap values when no `None` or `Err` is returned. - -### Known problems -There can be false positives if the function signature is designed to -fit some external requirement. - -### Example -``` -fn get_cool_number(a: bool, b: bool) -> Option { - if a && b { - return Some(50); - } - if a { - Some(0) - } else { - Some(10) - } -} -``` -Use instead: -``` -fn get_cool_number(a: bool, b: bool) -> i32 { - if a && b { - return 50; - } - if a { - 0 - } else { - 10 - } -} -``` \ No newline at end of file diff --git a/src/docs/unneeded_field_pattern.txt b/src/docs/unneeded_field_pattern.txt deleted file mode 100644 index 3cd00a0f3e38..000000000000 --- a/src/docs/unneeded_field_pattern.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for structure field patterns bound to wildcards. - -### Why is this bad? -Using `..` instead is shorter and leaves the focus on -the fields that are actually bound. - -### Example -``` -let f = Foo { a: 0, b: 0, c: 0 }; - -match f { - Foo { a: _, b: 0, .. } => {}, - Foo { a: _, b: _, c: _ } => {}, -} -``` - -Use instead: -``` -let f = Foo { a: 0, b: 0, c: 0 }; - -match f { - Foo { b: 0, .. } => {}, - Foo { .. } => {}, -} -``` \ No newline at end of file diff --git a/src/docs/unneeded_wildcard_pattern.txt b/src/docs/unneeded_wildcard_pattern.txt deleted file mode 100644 index 817061efd162..000000000000 --- a/src/docs/unneeded_wildcard_pattern.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for tuple patterns with a wildcard -pattern (`_`) is next to a rest pattern (`..`). - -_NOTE_: While `_, ..` means there is at least one element left, `..` -means there are 0 or more elements left. This can make a difference -when refactoring, but shouldn't result in errors in the refactored code, -since the wildcard pattern isn't used anyway. - -### Why is this bad? -The wildcard pattern is unneeded as the rest pattern -can match that element as well. - -### Example -``` -match t { - TupleStruct(0, .., _) => (), - _ => (), -} -``` - -Use instead: -``` -match t { - TupleStruct(0, ..) => (), - _ => (), -} -``` \ No newline at end of file diff --git a/src/docs/unnested_or_patterns.txt b/src/docs/unnested_or_patterns.txt deleted file mode 100644 index 49c45d4ee5e9..000000000000 --- a/src/docs/unnested_or_patterns.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and -suggests replacing the pattern with a nested one, `Some(0 | 2)`. - -Another way to think of this is that it rewrites patterns in -*disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*. - -### Why is this bad? -In the example above, `Some` is repeated, which unnecessarily complicates the pattern. - -### Example -``` -fn main() { - if let Some(0) | Some(2) = Some(0) {} -} -``` -Use instead: -``` -fn main() { - if let Some(0 | 2) = Some(0) {} -} -``` \ No newline at end of file diff --git a/src/docs/unreachable.txt b/src/docs/unreachable.txt deleted file mode 100644 index 10469ca77454..000000000000 --- a/src/docs/unreachable.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for usage of `unreachable!`. - -### Why is this bad? -This macro can cause code to panic - -### Example -``` -unreachable!(); -``` \ No newline at end of file diff --git a/src/docs/unreadable_literal.txt b/src/docs/unreadable_literal.txt deleted file mode 100644 index e168f90a84c1..000000000000 --- a/src/docs/unreadable_literal.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Warns if a long integral or floating-point constant does -not contain underscores. - -### Why is this bad? -Reading long numbers is difficult without separators. - -### Example -``` -61864918973511 -``` - -Use instead: -``` -61_864_918_973_511 -``` \ No newline at end of file diff --git a/src/docs/unsafe_derive_deserialize.txt b/src/docs/unsafe_derive_deserialize.txt deleted file mode 100644 index f56c48044f4f..000000000000 --- a/src/docs/unsafe_derive_deserialize.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for deriving `serde::Deserialize` on a type that -has methods using `unsafe`. - -### Why is this bad? -Deriving `serde::Deserialize` will create a constructor -that may violate invariants hold by another constructor. - -### Example -``` -use serde::Deserialize; - -#[derive(Deserialize)] -pub struct Foo { - // .. -} - -impl Foo { - pub fn new() -> Self { - // setup here .. - } - - pub unsafe fn parts() -> (&str, &str) { - // assumes invariants hold - } -} -``` \ No newline at end of file diff --git a/src/docs/unsafe_removed_from_name.txt b/src/docs/unsafe_removed_from_name.txt deleted file mode 100644 index 6f55c1815dd6..000000000000 --- a/src/docs/unsafe_removed_from_name.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for imports that remove "unsafe" from an item's -name. - -### Why is this bad? -Renaming makes it less clear which traits and -structures are unsafe. - -### Example -``` -use std::cell::{UnsafeCell as TotallySafeCell}; - -extern crate crossbeam; -use crossbeam::{spawn_unsafe as spawn}; -``` \ No newline at end of file diff --git a/src/docs/unseparated_literal_suffix.txt b/src/docs/unseparated_literal_suffix.txt deleted file mode 100644 index d80248e34d0c..000000000000 --- a/src/docs/unseparated_literal_suffix.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Warns if literal suffixes are not separated by an -underscore. -To enforce unseparated literal suffix style, -see the `separated_literal_suffix` lint. - -### Why is this bad? -Suffix style should be consistent. - -### Example -``` -123832i32 -``` - -Use instead: -``` -123832_i32 -``` \ No newline at end of file diff --git a/src/docs/unsound_collection_transmute.txt b/src/docs/unsound_collection_transmute.txt deleted file mode 100644 index 29db9258e83f..000000000000 --- a/src/docs/unsound_collection_transmute.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for transmutes between collections whose -types have different ABI, size or alignment. - -### Why is this bad? -This is undefined behavior. - -### Known problems -Currently, we cannot know whether a type is a -collection, so we just lint the ones that come with `std`. - -### Example -``` -// different size, therefore likely out-of-bounds memory access -// You absolutely do not want this in your code! -unsafe { - std::mem::transmute::<_, Vec>(vec![2_u16]) -}; -``` - -You must always iterate, map and collect the values: - -``` -vec![2_u16].into_iter().map(u32::from).collect::>(); -``` \ No newline at end of file diff --git a/src/docs/unused_async.txt b/src/docs/unused_async.txt deleted file mode 100644 index 26def11aa17a..000000000000 --- a/src/docs/unused_async.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for functions that are declared `async` but have no `.await`s inside of them. - -### Why is this bad? -Async functions with no async code create overhead, both mentally and computationally. -Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which -causes runtime overhead and hassle for the caller. - -### Example -``` -async fn get_random_number() -> i64 { - 4 // Chosen by fair dice roll. Guaranteed to be random. -} -let number_future = get_random_number(); -``` - -Use instead: -``` -fn get_random_number_improved() -> i64 { - 4 // Chosen by fair dice roll. Guaranteed to be random. -} -let number_future = async { get_random_number_improved() }; -``` \ No newline at end of file diff --git a/src/docs/unused_format_specs.txt b/src/docs/unused_format_specs.txt deleted file mode 100644 index 77be3a2fb170..000000000000 --- a/src/docs/unused_format_specs.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Detects [formatting parameters] that have no effect on the output of -`format!()`, `println!()` or similar macros. - -### Why is this bad? -Shorter format specifiers are easier to read, it may also indicate that -an expected formatting operation such as adding padding isn't happening. - -### Example -``` -println!("{:.}", 1.0); - -println!("not padded: {:5}", format_args!("...")); -``` -Use instead: -``` -println!("{}", 1.0); - -println!("not padded: {}", format_args!("...")); -// OR -println!("padded: {:5}", format!("...")); -``` - -[formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters \ No newline at end of file diff --git a/src/docs/unused_io_amount.txt b/src/docs/unused_io_amount.txt deleted file mode 100644 index fbc4c299c7bb..000000000000 --- a/src/docs/unused_io_amount.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Checks for unused written/read amount. - -### Why is this bad? -`io::Write::write(_vectored)` and -`io::Read::read(_vectored)` are not guaranteed to -process the entire buffer. They return how many bytes were processed, which -might be smaller -than a given buffer's length. If you don't need to deal with -partial-write/read, use -`write_all`/`read_exact` instead. - -When working with asynchronous code (either with the `futures` -crate or with `tokio`), a similar issue exists for -`AsyncWriteExt::write()` and `AsyncReadExt::read()` : these -functions are also not guaranteed to process the entire -buffer. Your code should either handle partial-writes/reads, or -call the `write_all`/`read_exact` methods on those traits instead. - -### Known problems -Detects only common patterns. - -### Examples -``` -use std::io; -fn foo(w: &mut W) -> io::Result<()> { - // must be `w.write_all(b"foo")?;` - w.write(b"foo")?; - Ok(()) -} -``` \ No newline at end of file diff --git a/src/docs/unused_peekable.txt b/src/docs/unused_peekable.txt deleted file mode 100644 index 268de1ce3bec..000000000000 --- a/src/docs/unused_peekable.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for the creation of a `peekable` iterator that is never `.peek()`ed - -### Why is this bad? -Creating a peekable iterator without using any of its methods is likely a mistake, -or just a leftover after a refactor. - -### Example -``` -let collection = vec![1, 2, 3]; -let iter = collection.iter().peekable(); - -for item in iter { - // ... -} -``` - -Use instead: -``` -let collection = vec![1, 2, 3]; -let iter = collection.iter(); - -for item in iter { - // ... -} -``` \ No newline at end of file diff --git a/src/docs/unused_rounding.txt b/src/docs/unused_rounding.txt deleted file mode 100644 index 70947aceee77..000000000000 --- a/src/docs/unused_rounding.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does - -Detects cases where a whole-number literal float is being rounded, using -the `floor`, `ceil`, or `round` methods. - -### Why is this bad? - -This is unnecessary and confusing to the reader. Doing this is probably a mistake. - -### Example -``` -let x = 1f32.ceil(); -``` -Use instead: -``` -let x = 1f32; -``` \ No newline at end of file diff --git a/src/docs/unused_self.txt b/src/docs/unused_self.txt deleted file mode 100644 index a8d0fc759895..000000000000 --- a/src/docs/unused_self.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks methods that contain a `self` argument but don't use it - -### Why is this bad? -It may be clearer to define the method as an associated function instead -of an instance method if it doesn't require `self`. - -### Example -``` -struct A; -impl A { - fn method(&self) {} -} -``` - -Could be written: - -``` -struct A; -impl A { - fn method() {} -} -``` \ No newline at end of file diff --git a/src/docs/unused_unit.txt b/src/docs/unused_unit.txt deleted file mode 100644 index 48d16ca65523..000000000000 --- a/src/docs/unused_unit.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for unit (`()`) expressions that can be removed. - -### Why is this bad? -Such expressions add no value, but can make the code -less readable. Depending on formatting they can make a `break` or `return` -statement look like a function call. - -### Example -``` -fn return_unit() -> () { - () -} -``` -is equivalent to -``` -fn return_unit() {} -``` \ No newline at end of file diff --git a/src/docs/unusual_byte_groupings.txt b/src/docs/unusual_byte_groupings.txt deleted file mode 100644 index 9a1f132a6112..000000000000 --- a/src/docs/unusual_byte_groupings.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Warns if hexadecimal or binary literals are not grouped -by nibble or byte. - -### Why is this bad? -Negatively impacts readability. - -### Example -``` -let x: u32 = 0xFFF_FFF; -let y: u8 = 0b01_011_101; -``` \ No newline at end of file diff --git a/src/docs/unwrap_in_result.txt b/src/docs/unwrap_in_result.txt deleted file mode 100644 index 7497dd863d35..000000000000 --- a/src/docs/unwrap_in_result.txt +++ /dev/null @@ -1,39 +0,0 @@ -### What it does -Checks for functions of type `Result` that contain `expect()` or `unwrap()` - -### Why is this bad? -These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics. - -### Known problems -This can cause false positives in functions that handle both recoverable and non recoverable errors. - -### Example -Before: -``` -fn divisible_by_3(i_str: String) -> Result<(), String> { - let i = i_str - .parse::() - .expect("cannot divide the input by three"); - - if i % 3 != 0 { - Err("Number is not divisible by 3")? - } - - Ok(()) -} -``` - -After: -``` -fn divisible_by_3(i_str: String) -> Result<(), String> { - let i = i_str - .parse::() - .map_err(|e| format!("cannot divide the input by three: {}", e))?; - - if i % 3 != 0 { - Err("Number is not divisible by 3")? - } - - Ok(()) -} -``` \ No newline at end of file diff --git a/src/docs/unwrap_or_else_default.txt b/src/docs/unwrap_or_else_default.txt deleted file mode 100644 index 34b4cf088581..000000000000 --- a/src/docs/unwrap_or_else_default.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and -`Result` values. - -### Why is this bad? -Readability, these can be written as `_.unwrap_or_default`, which is -simpler and more concise. - -### Examples -``` -x.unwrap_or_else(Default::default); -x.unwrap_or_else(u32::default); -``` - -Use instead: -``` -x.unwrap_or_default(); -``` \ No newline at end of file diff --git a/src/docs/unwrap_used.txt b/src/docs/unwrap_used.txt deleted file mode 100644 index 9b4713df515d..000000000000 --- a/src/docs/unwrap_used.txt +++ /dev/null @@ -1,37 +0,0 @@ -### What it does -Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s. - -### Why is this bad? -It is better to handle the `None` or `Err` case, -or at least call `.expect(_)` with a more helpful message. Still, for a lot of -quick-and-dirty code, `unwrap` is a good choice, which is why this lint is -`Allow` by default. - -`result.unwrap()` will let the thread panic on `Err` values. -Normally, you want to implement more sophisticated error handling, -and propagate errors upwards with `?` operator. - -Even if you want to panic on errors, not all `Error`s implement good -messages on display. Therefore, it may be beneficial to look at the places -where they may get displayed. Activate this lint to do just that. - -### Examples -``` -option.unwrap(); -result.unwrap(); -``` - -Use instead: -``` -option.expect("more helpful message"); -result.expect("more helpful message"); -``` - -If [expect_used](#expect_used) is enabled, instead: -``` -option?; - -// or - -result?; -``` \ No newline at end of file diff --git a/src/docs/upper_case_acronyms.txt b/src/docs/upper_case_acronyms.txt deleted file mode 100644 index a1e39c7e10c6..000000000000 --- a/src/docs/upper_case_acronyms.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for fully capitalized names and optionally names containing a capitalized acronym. - -### Why is this bad? -In CamelCase, acronyms count as one word. -See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case) -for more. - -By default, the lint only triggers on fully-capitalized names. -You can use the `upper-case-acronyms-aggressive: true` config option to enable linting -on all camel case names - -### Known problems -When two acronyms are contiguous, the lint can't tell where -the first acronym ends and the second starts, so it suggests to lowercase all of -the letters in the second acronym. - -### Example -``` -struct HTTPResponse; -``` -Use instead: -``` -struct HttpResponse; -``` \ No newline at end of file diff --git a/src/docs/use_debug.txt b/src/docs/use_debug.txt deleted file mode 100644 index 94d4a6fd29ae..000000000000 --- a/src/docs/use_debug.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for use of `Debug` formatting. The purpose of this -lint is to catch debugging remnants. - -### Why is this bad? -The purpose of the `Debug` trait is to facilitate -debugging Rust code. It should not be used in user-facing output. - -### Example -``` -println!("{:?}", foo); -``` \ No newline at end of file diff --git a/src/docs/use_self.txt b/src/docs/use_self.txt deleted file mode 100644 index bd37ed1e0025..000000000000 --- a/src/docs/use_self.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Checks for unnecessary repetition of structure name when a -replacement with `Self` is applicable. - -### Why is this bad? -Unnecessary repetition. Mixed use of `Self` and struct -name -feels inconsistent. - -### Known problems -- Unaddressed false negative in fn bodies of trait implementations -- False positive with associated types in traits (#4140) - -### Example -``` -struct Foo; -impl Foo { - fn new() -> Foo { - Foo {} - } -} -``` -could be -``` -struct Foo; -impl Foo { - fn new() -> Self { - Self {} - } -} -``` \ No newline at end of file diff --git a/src/docs/used_underscore_binding.txt b/src/docs/used_underscore_binding.txt deleted file mode 100644 index ed67c41eb0db..000000000000 --- a/src/docs/used_underscore_binding.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for the use of bindings with a single leading -underscore. - -### Why is this bad? -A single leading underscore is usually used to indicate -that a binding will not be used. Using such a binding breaks this -expectation. - -### Known problems -The lint does not work properly with desugaring and -macro, it has been allowed in the mean time. - -### Example -``` -let _x = 0; -let y = _x + 1; // Here we are using `_x`, even though it has a leading - // underscore. We should rename `_x` to `x` -``` \ No newline at end of file diff --git a/src/docs/useless_asref.txt b/src/docs/useless_asref.txt deleted file mode 100644 index f777cd3775ed..000000000000 --- a/src/docs/useless_asref.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for usage of `.as_ref()` or `.as_mut()` where the -types before and after the call are the same. - -### Why is this bad? -The call is unnecessary. - -### Example -``` -let x: &[i32] = &[1, 2, 3, 4, 5]; -do_stuff(x.as_ref()); -``` -The correct use would be: -``` -let x: &[i32] = &[1, 2, 3, 4, 5]; -do_stuff(x); -``` \ No newline at end of file diff --git a/src/docs/useless_attribute.txt b/src/docs/useless_attribute.txt deleted file mode 100644 index e02d4c907898..000000000000 --- a/src/docs/useless_attribute.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks for `extern crate` and `use` items annotated with -lint attributes. - -This lint permits lint attributes for lints emitted on the items themself. -For `use` items these lints are: -* deprecated -* unreachable_pub -* unused_imports -* clippy::enum_glob_use -* clippy::macro_use_imports -* clippy::wildcard_imports - -For `extern crate` items these lints are: -* `unused_imports` on items with `#[macro_use]` - -### Why is this bad? -Lint attributes have no effect on crate imports. Most -likely a `!` was forgotten. - -### Example -``` -#[deny(dead_code)] -extern crate foo; -#[forbid(dead_code)] -use foo::bar; -``` - -Use instead: -``` -#[allow(unused_imports)] -use foo::baz; -#[allow(unused_imports)] -#[macro_use] -extern crate baz; -``` \ No newline at end of file diff --git a/src/docs/useless_conversion.txt b/src/docs/useless_conversion.txt deleted file mode 100644 index 06000a7ad98d..000000000000 --- a/src/docs/useless_conversion.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls -which uselessly convert to the same type. - -### Why is this bad? -Redundant code. - -### Example -``` -// format!() returns a `String` -let s: String = format!("hello").into(); -``` - -Use instead: -``` -let s: String = format!("hello"); -``` \ No newline at end of file diff --git a/src/docs/useless_format.txt b/src/docs/useless_format.txt deleted file mode 100644 index eb4819da1e95..000000000000 --- a/src/docs/useless_format.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for the use of `format!("string literal with no -argument")` and `format!("{}", foo)` where `foo` is a string. - -### Why is this bad? -There is no point of doing that. `format!("foo")` can -be replaced by `"foo".to_owned()` if you really need a `String`. The even -worse `&format!("foo")` is often encountered in the wild. `format!("{}", -foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()` -if `foo: &str`. - -### Examples -``` -let foo = "foo"; -format!("{}", foo); -``` - -Use instead: -``` -let foo = "foo"; -foo.to_owned(); -``` \ No newline at end of file diff --git a/src/docs/useless_let_if_seq.txt b/src/docs/useless_let_if_seq.txt deleted file mode 100644 index c6dcd57eb2e2..000000000000 --- a/src/docs/useless_let_if_seq.txt +++ /dev/null @@ -1,39 +0,0 @@ -### What it does -Checks for variable declarations immediately followed by a -conditional affectation. - -### Why is this bad? -This is not idiomatic Rust. - -### Example -``` -let foo; - -if bar() { - foo = 42; -} else { - foo = 0; -} - -let mut baz = None; - -if bar() { - baz = Some(42); -} -``` - -should be written - -``` -let foo = if bar() { - 42 -} else { - 0 -}; - -let baz = if bar() { - Some(42) -} else { - None -}; -``` \ No newline at end of file diff --git a/src/docs/useless_transmute.txt b/src/docs/useless_transmute.txt deleted file mode 100644 index 1d3a17588145..000000000000 --- a/src/docs/useless_transmute.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for transmutes to the original type of the object -and transmutes that could be a cast. - -### Why is this bad? -Readability. The code tricks people into thinking that -something complex is going on. - -### Example -``` -core::intrinsics::transmute(t); // where the result type is the same as `t`'s -``` \ No newline at end of file diff --git a/src/docs/useless_vec.txt b/src/docs/useless_vec.txt deleted file mode 100644 index ee5afc99e4bf..000000000000 --- a/src/docs/useless_vec.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for usage of `&vec![..]` when using `&[..]` would -be possible. - -### Why is this bad? -This is less efficient. - -### Example -``` -fn foo(_x: &[u8]) {} - -foo(&vec![1, 2]); -``` - -Use instead: -``` -foo(&[1, 2]); -``` \ No newline at end of file diff --git a/src/docs/vec_box.txt b/src/docs/vec_box.txt deleted file mode 100644 index 701b1c9ce9b9..000000000000 --- a/src/docs/vec_box.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for use of `Vec>` where T: Sized anywhere in the code. -Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. - -### Why is this bad? -`Vec` already keeps its contents in a separate area on -the heap. So if you `Box` its contents, you just add another level of indirection. - -### Known problems -Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530), -1st comment). - -### Example -``` -struct X { - values: Vec>, -} -``` - -Better: - -``` -struct X { - values: Vec, -} -``` \ No newline at end of file diff --git a/src/docs/vec_init_then_push.txt b/src/docs/vec_init_then_push.txt deleted file mode 100644 index 445f2874796b..000000000000 --- a/src/docs/vec_init_then_push.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for calls to `push` immediately after creating a new `Vec`. - -If the `Vec` is created using `with_capacity` this will only lint if the capacity is a -constant and the number of pushes is greater than or equal to the initial capacity. - -If the `Vec` is extended after the initial sequence of pushes and it was default initialized -then this will only lint after there were at least four pushes. This number may change in -the future. - -### Why is this bad? -The `vec![]` macro is both more performant and easier to read than -multiple `push` calls. - -### Example -``` -let mut v = Vec::new(); -v.push(0); -``` -Use instead: -``` -let v = vec![0]; -``` \ No newline at end of file diff --git a/src/docs/vec_resize_to_zero.txt b/src/docs/vec_resize_to_zero.txt deleted file mode 100644 index 0b92686772bb..000000000000 --- a/src/docs/vec_resize_to_zero.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Finds occurrences of `Vec::resize(0, an_int)` - -### Why is this bad? -This is probably an argument inversion mistake. - -### Example -``` -vec!(1, 2, 3, 4, 5).resize(0, 5) -``` - -Use instead: -``` -vec!(1, 2, 3, 4, 5).clear() -``` \ No newline at end of file diff --git a/src/docs/verbose_bit_mask.txt b/src/docs/verbose_bit_mask.txt deleted file mode 100644 index 87a84702925e..000000000000 --- a/src/docs/verbose_bit_mask.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for bit masks that can be replaced by a call -to `trailing_zeros` - -### Why is this bad? -`x.trailing_zeros() > 4` is much clearer than `x & 15 -== 0` - -### Known problems -llvm generates better code for `x & 15 == 0` on x86 - -### Example -``` -if x & 0b1111 == 0 { } -``` \ No newline at end of file diff --git a/src/docs/verbose_file_reads.txt b/src/docs/verbose_file_reads.txt deleted file mode 100644 index 9703df423a57..000000000000 --- a/src/docs/verbose_file_reads.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for use of File::read_to_end and File::read_to_string. - -### Why is this bad? -`fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values. -See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html) - -### Example -``` -let mut f = File::open("foo.txt").unwrap(); -let mut bytes = Vec::new(); -f.read_to_end(&mut bytes).unwrap(); -``` -Can be written more concisely as -``` -let mut bytes = fs::read("foo.txt").unwrap(); -``` \ No newline at end of file diff --git a/src/docs/vtable_address_comparisons.txt b/src/docs/vtable_address_comparisons.txt deleted file mode 100644 index 4a34e4ba78ef..000000000000 --- a/src/docs/vtable_address_comparisons.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for comparisons with an address of a trait vtable. - -### Why is this bad? -Comparing trait objects pointers compares an vtable addresses which -are not guaranteed to be unique and could vary between different code generation units. -Furthermore vtables for different types could have the same address after being merged -together. - -### Example -``` -let a: Rc = ... -let b: Rc = ... -if Rc::ptr_eq(&a, &b) { - ... -} -``` \ No newline at end of file diff --git a/src/docs/while_immutable_condition.txt b/src/docs/while_immutable_condition.txt deleted file mode 100644 index 71800701f489..000000000000 --- a/src/docs/while_immutable_condition.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks whether variables used within while loop condition -can be (and are) mutated in the body. - -### Why is this bad? -If the condition is unchanged, entering the body of the loop -will lead to an infinite loop. - -### Known problems -If the `while`-loop is in a closure, the check for mutation of the -condition variables in the body can cause false negatives. For example when only `Upvar` `a` is -in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger. - -### Example -``` -let i = 0; -while i > 10 { - println!("let me loop forever!"); -} -``` \ No newline at end of file diff --git a/src/docs/while_let_loop.txt b/src/docs/while_let_loop.txt deleted file mode 100644 index ab7bf60975ec..000000000000 --- a/src/docs/while_let_loop.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Detects `loop + match` combinations that are easier -written as a `while let` loop. - -### Why is this bad? -The `while let` loop is usually shorter and more -readable. - -### Known problems -Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)). - -### Example -``` -loop { - let x = match y { - Some(x) => x, - None => break, - }; - // .. do something with x -} -// is easier written as -while let Some(x) = y { - // .. do something with x -}; -``` \ No newline at end of file diff --git a/src/docs/while_let_on_iterator.txt b/src/docs/while_let_on_iterator.txt deleted file mode 100644 index af053c541199..000000000000 --- a/src/docs/while_let_on_iterator.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for `while let` expressions on iterators. - -### Why is this bad? -Readability. A simple `for` loop is shorter and conveys -the intent better. - -### Example -``` -while let Some(val) = iter.next() { - .. -} -``` - -Use instead: -``` -for val in &mut iter { - .. -} -``` \ No newline at end of file diff --git a/src/docs/wildcard_dependencies.txt b/src/docs/wildcard_dependencies.txt deleted file mode 100644 index 2affaf9740d9..000000000000 --- a/src/docs/wildcard_dependencies.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for wildcard dependencies in the `Cargo.toml`. - -### Why is this bad? -[As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html), -it is highly unlikely that you work with any possible version of your dependency, -and wildcard dependencies would cause unnecessary breakage in the ecosystem. - -### Example -``` -[dependencies] -regex = "*" -``` \ No newline at end of file diff --git a/src/docs/wildcard_enum_match_arm.txt b/src/docs/wildcard_enum_match_arm.txt deleted file mode 100644 index 09807c01c652..000000000000 --- a/src/docs/wildcard_enum_match_arm.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for wildcard enum matches using `_`. - -### Why is this bad? -New enum variants added by library updates can be missed. - -### Known problems -Suggested replacements may be incorrect if guards exhaustively cover some -variants, and also may not use correct path to enum if it's not present in the current scope. - -### Example -``` -match x { - Foo::A(_) => {}, - _ => {}, -} -``` - -Use instead: -``` -match x { - Foo::A(_) => {}, - Foo::B(_) => {}, -} -``` \ No newline at end of file diff --git a/src/docs/wildcard_imports.txt b/src/docs/wildcard_imports.txt deleted file mode 100644 index bd56aa5b0828..000000000000 --- a/src/docs/wildcard_imports.txt +++ /dev/null @@ -1,45 +0,0 @@ -### What it does -Checks for wildcard imports `use _::*`. - -### Why is this bad? -wildcard imports can pollute the namespace. This is especially bad if -you try to import something through a wildcard, that already has been imported by name from -a different source: - -``` -use crate1::foo; // Imports a function named foo -use crate2::*; // Has a function named foo - -foo(); // Calls crate1::foo -``` - -This can lead to confusing error messages at best and to unexpected behavior at worst. - -### Exceptions -Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library) -provide modules named "prelude" specifically designed for wildcard import. - -`use super::*` is allowed in test modules. This is defined as any module with "test" in the name. - -These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag. - -### Known problems -If macros are imported through the wildcard, this macro is not included -by the suggestion and has to be added by hand. - -Applying the suggestion when explicit imports of the things imported with a glob import -exist, may result in `unused_imports` warnings. - -### Example -``` -use crate1::*; - -foo(); -``` - -Use instead: -``` -use crate1::foo; - -foo(); -``` \ No newline at end of file diff --git a/src/docs/wildcard_in_or_patterns.txt b/src/docs/wildcard_in_or_patterns.txt deleted file mode 100644 index 70468ca41e0b..000000000000 --- a/src/docs/wildcard_in_or_patterns.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for wildcard pattern used with others patterns in same match arm. - -### Why is this bad? -Wildcard pattern already covers any other pattern as it will match anyway. -It makes the code less readable, especially to spot wildcard pattern use in match arm. - -### Example -``` -match s { - "a" => {}, - "bar" | _ => {}, -} -``` - -Use instead: -``` -match s { - "a" => {}, - _ => {}, -} -``` \ No newline at end of file diff --git a/src/docs/write_literal.txt b/src/docs/write_literal.txt deleted file mode 100644 index a7a884d08711..000000000000 --- a/src/docs/write_literal.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -This lint warns about the use of literals as `write!`/`writeln!` args. - -### Why is this bad? -Using literals as `writeln!` args is inefficient -(c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary -(i.e., just put the literal in the format string) - -### Example -``` -writeln!(buf, "{}", "foo"); -``` - -Use instead: -``` -writeln!(buf, "foo"); -``` \ No newline at end of file diff --git a/src/docs/write_with_newline.txt b/src/docs/write_with_newline.txt deleted file mode 100644 index 22845fd6515b..000000000000 --- a/src/docs/write_with_newline.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -This lint warns when you use `write!()` with a format -string that -ends in a newline. - -### Why is this bad? -You should use `writeln!()` instead, which appends the -newline. - -### Example -``` -write!(buf, "Hello {}!\n", name); -``` - -Use instead: -``` -writeln!(buf, "Hello {}!", name); -``` \ No newline at end of file diff --git a/src/docs/writeln_empty_string.txt b/src/docs/writeln_empty_string.txt deleted file mode 100644 index 3b3aeb79a48b..000000000000 --- a/src/docs/writeln_empty_string.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns when you use `writeln!(buf, "")` to -print a newline. - -### Why is this bad? -You should use `writeln!(buf)`, which is simpler. - -### Example -``` -writeln!(buf, ""); -``` - -Use instead: -``` -writeln!(buf); -``` \ No newline at end of file diff --git a/src/docs/wrong_self_convention.txt b/src/docs/wrong_self_convention.txt deleted file mode 100644 index d6b69ab87f86..000000000000 --- a/src/docs/wrong_self_convention.txt +++ /dev/null @@ -1,39 +0,0 @@ -### What it does -Checks for methods with certain name prefixes and which -doesn't match how self is taken. The actual rules are: - -|Prefix |Postfix |`self` taken | `self` type | -|-------|------------|-------------------------------|--------------| -|`as_` | none |`&self` or `&mut self` | any | -|`from_`| none | none | any | -|`into_`| none |`self` | any | -|`is_` | none |`&mut self` or `&self` or none | any | -|`to_` | `_mut` |`&mut self` | any | -|`to_` | not `_mut` |`self` | `Copy` | -|`to_` | not `_mut` |`&self` | not `Copy` | - -Note: Clippy doesn't trigger methods with `to_` prefix in: -- Traits definition. -Clippy can not tell if a type that implements a trait is `Copy` or not. -- Traits implementation, when `&self` is taken. -The method signature is controlled by the trait and often `&self` is required for all types that implement the trait -(see e.g. the `std::string::ToString` trait). - -Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required. - -Please find more info here: -https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv - -### Why is this bad? -Consistency breeds readability. If you follow the -conventions, your users won't be surprised that they, e.g., need to supply a -mutable reference to a `as_..` function. - -### Example -``` -impl X { - fn as_str(self) -> &'static str { - // .. - } -} -``` \ No newline at end of file diff --git a/src/docs/wrong_transmute.txt b/src/docs/wrong_transmute.txt deleted file mode 100644 index 9fc71e0e382e..000000000000 --- a/src/docs/wrong_transmute.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for transmutes that can't ever be correct on any -architecture. - -### Why is this bad? -It's basically guaranteed to be undefined behavior. - -### Known problems -When accessing C, users might want to store pointer -sized objects in `extradata` arguments to save an allocation. - -### Example -``` -let ptr: *const T = core::intrinsics::transmute('x') -``` \ No newline at end of file diff --git a/src/docs/zero_divided_by_zero.txt b/src/docs/zero_divided_by_zero.txt deleted file mode 100644 index 394de20c0c0c..000000000000 --- a/src/docs/zero_divided_by_zero.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for `0.0 / 0.0`. - -### Why is this bad? -It's less readable than `f32::NAN` or `f64::NAN`. - -### Example -``` -let nan = 0.0f32 / 0.0; -``` - -Use instead: -``` -let nan = f32::NAN; -``` \ No newline at end of file diff --git a/src/docs/zero_prefixed_literal.txt b/src/docs/zero_prefixed_literal.txt deleted file mode 100644 index 5c5588725363..000000000000 --- a/src/docs/zero_prefixed_literal.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Warns if an integral constant literal starts with `0`. - -### Why is this bad? -In some languages (including the infamous C language -and most of its -family), this marks an octal constant. In Rust however, this is a decimal -constant. This could -be confusing for both the writer and a reader of the constant. - -### Example - -In Rust: -``` -fn main() { - let a = 0123; - println!("{}", a); -} -``` - -prints `123`, while in C: - -``` -#include - -int main() { - int a = 0123; - printf("%d\n", a); -} -``` - -prints `83` (as `83 == 0o123` while `123 == 0o173`). \ No newline at end of file diff --git a/src/docs/zero_ptr.txt b/src/docs/zero_ptr.txt deleted file mode 100644 index e768a0236600..000000000000 --- a/src/docs/zero_ptr.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Catch casts from `0` to some pointer type - -### Why is this bad? -This generally means `null` and is better expressed as -{`std`, `core`}`::ptr::`{`null`, `null_mut`}. - -### Example -``` -let a = 0 as *const u32; -``` - -Use instead: -``` -let a = std::ptr::null::(); -``` \ No newline at end of file diff --git a/src/docs/zero_sized_map_values.txt b/src/docs/zero_sized_map_values.txt deleted file mode 100644 index 0502bdbf3950..000000000000 --- a/src/docs/zero_sized_map_values.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for maps with zero-sized value types anywhere in the code. - -### Why is this bad? -Since there is only a single value for a zero-sized type, a map -containing zero sized values is effectively a set. Using a set in that case improves -readability and communicates intent more clearly. - -### Known problems -* A zero-sized type cannot be recovered later if it contains private fields. -* This lints the signature of public items - -### Example -``` -fn unique_words(text: &str) -> HashMap<&str, ()> { - todo!(); -} -``` -Use instead: -``` -fn unique_words(text: &str) -> HashSet<&str> { - todo!(); -} -``` \ No newline at end of file diff --git a/src/docs/zst_offset.txt b/src/docs/zst_offset.txt deleted file mode 100644 index 5810455ee955..000000000000 --- a/src/docs/zst_offset.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to -zero-sized types - -### Why is this bad? -This is a no-op, and likely unintended - -### Example -``` -unsafe { (&() as *const ()).offset(1) }; -``` \ No newline at end of file diff --git a/src/driver.rs b/src/driver.rs index f24d3507823e..ee2a3ad20d3e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,4 +1,5 @@ #![feature(rustc_private)] +#![feature(let_chains)] #![feature(once_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap @@ -71,6 +72,32 @@ fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option) )); } +/// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy +/// when any of them are modified +fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option) { + let file_depinfo = parse_sess.file_depinfo.get_mut(); + + // Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver` + // with the current directory set to `CARGO_MANIFEST_DIR` so a relative path is fine + if Path::new("Cargo.toml").exists() { + file_depinfo.insert(Symbol::intern("Cargo.toml")); + } + + // `clippy.toml` + if let Some(path) = conf_path_string { + file_depinfo.insert(Symbol::intern(&path)); + } + + // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever + // it is rebuilt + if cfg!(debug_assertions) + && let Ok(current_exe) = env::current_exe() + && let Some(current_exe) = current_exe.to_str() + { + file_depinfo.insert(Symbol::intern(current_exe)); + } +} + struct DefaultCallbacks; impl rustc_driver::Callbacks for DefaultCallbacks {} @@ -97,10 +124,18 @@ impl rustc_driver::Callbacks for ClippyCallbacks { // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level` #[allow(rustc::bad_opt_access)] fn config(&mut self, config: &mut interface::Config) { + let conf_path = clippy_lints::lookup_conf_file(); + let conf_path_string = if let Ok(Some(path)) = &conf_path { + path.to_str().map(String::from) + } else { + None + }; + let previous = config.register_lints.take(); let clippy_args_var = self.clippy_args_var.take(); config.parse_sess_created = Some(Box::new(move |parse_sess| { track_clippy_args(parse_sess, &clippy_args_var); + track_files(parse_sess, conf_path_string); })); config.register_lints = Some(Box::new(move |sess, lint_store| { // technically we're ~guaranteed that this is none but might as well call anything that @@ -109,7 +144,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { (previous)(sess, lint_store); } - let conf = clippy_lints::read_conf(sess); + let conf = clippy_lints::read_conf(sess, &conf_path); clippy_lints::register_plugins(lint_store, sess, &conf); clippy_lints::register_pre_expansion_lints(lint_store, sess, &conf); clippy_lints::register_renamed(lint_store); @@ -217,6 +252,13 @@ pub fn main() { exit(rustc_driver::catch_with_exit_code(move || { let mut orig_args: Vec = env::args().collect(); + let sys_root_env = std::env::var("SYSROOT").ok(); + let pass_sysroot_env_if_given = |args: &mut Vec, sys_root_env| { + if let Some(sys_root) = sys_root_env { + args.extend(vec!["--sysroot".into(), sys_root]); + }; + }; + // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc" // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver // uses @@ -224,7 +266,10 @@ pub fn main() { orig_args.remove(pos); orig_args[0] = "rustc".to_string(); - return rustc_driver::RunCompiler::new(&orig_args, &mut DefaultCallbacks).run(); + let mut args: Vec = orig_args.clone(); + pass_sysroot_env_if_given(&mut args, sys_root_env); + + return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run(); } if orig_args.iter().any(|a| a == "--version" || a == "-V") { @@ -247,6 +292,9 @@ pub fn main() { exit(0); } + let mut args: Vec = orig_args.clone(); + pass_sysroot_env_if_given(&mut args, sys_root_env); + let mut no_deps = false; let clippy_args_var = env::var("CLIPPY_ARGS").ok(); let clippy_args = clippy_args_var @@ -275,11 +323,10 @@ pub fn main() { let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package); if clippy_enabled { - let mut args: Vec = orig_args.clone(); args.extend(clippy_args); rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run() } else { - rustc_driver::RunCompiler::new(&orig_args, &mut RustcCallbacks { clippy_args_var }).run() + rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run() } })) } diff --git a/src/main.rs b/src/main.rs index fce3cdfc462e..d418d2daa313 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,8 +7,6 @@ use std::env; use std::path::PathBuf; use std::process::{self, Command}; -mod docs; - const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code. Usage: @@ -60,7 +58,7 @@ pub fn main() { if let Some(pos) = env::args().position(|a| a == "--explain") { if let Some(mut lint) = env::args().nth(pos + 1) { lint.make_ascii_lowercase(); - docs::explain(&lint.strip_prefix("clippy::").unwrap_or(&lint).replace('-', "_")); + clippy_lints::explain(&lint.strip_prefix("clippy::").unwrap_or(&lint).replace('-', "_")); } else { show_help(); } diff --git a/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr b/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr index 9a7d802dc6d3..163f8bb35e79 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr @@ -12,5 +12,11 @@ note: the lint level is defined here LL | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error; 1 warning emitted +error: unnecessary structure name repetition + --> $DIR/main.rs:7:9 + | +LL | Foo + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr b/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr index a280e1bacdfd..259d39b12526 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr @@ -10,5 +10,11 @@ note: the lint level is defined here LL | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: unnecessary structure name repetition + --> $DIR/main.rs:7:9 + | +LL | Foo + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors diff --git a/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr b/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr index a280e1bacdfd..259d39b12526 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr @@ -10,5 +10,11 @@ note: the lint level is defined here LL | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: unnecessary structure name repetition + --> $DIR/main.rs:7:9 + | +LL | Foo + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors diff --git a/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr b/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr index a280e1bacdfd..259d39b12526 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr @@ -10,5 +10,11 @@ note: the lint level is defined here LL | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: unnecessary structure name repetition + --> $DIR/main.rs:7:9 + | +LL | Foo + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors diff --git a/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr b/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr index 88f6e00922bc..97e6c3d5a559 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr @@ -10,5 +10,11 @@ note: the lint level is defined here LL | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: unnecessary structure name repetition + --> $DIR/main.rs:12:9 + | +LL | Foo + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs index 5057a018300e..4be04f77f5bd 100644 --- a/tests/ui-internal/custom_ice_message.rs +++ b/tests/ui-internal/custom_ice_message.rs @@ -2,6 +2,7 @@ // normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" // normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" // normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" +// normalize-stderr-test: "'rustc'" -> "''" #![deny(clippy::internal)] #![allow(clippy::missing_clippy_version_attribute)] diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index 07c5941013c1..2ba5890660fc 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,4 +1,4 @@ -thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9 +thread '' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 2a240cc249b0..8bfc060e9910 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -1,3 +1,12 @@ +error: hardcoded path to a language item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 + | +LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `LangItem::DerefMut` + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + error: hardcoded path to a diagnostic item --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 | @@ -5,7 +14,6 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: convert all references to use `sym::Deref` - = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` error: hardcoded path to a diagnostic item --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 @@ -15,13 +23,5 @@ LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", | = help: convert all references to use `sym::deref_method` -error: hardcoded path to a language item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 - | -LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `LangItem::DerefMut` - error: aborting due to 3 previous errors diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs index 1aed09b7c7bd..e8a023ab1764 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs @@ -1,6 +1,6 @@ #![warn(clippy::arithmetic_side_effects)] -use core::ops::Add; +use core::ops::{Add, Neg}; #[derive(Clone, Copy)] struct Point { @@ -16,9 +16,18 @@ impl Add for Point { } } +impl Neg for Point { + type Output = Self; + + fn neg(self) -> Self::Output { + todo!() + } +} + fn main() { let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 }; let point: Point = Point { x: 1, y: 0 }; let _ = point + point; + let _ = -point; } diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr index 4c75998437fd..825aa1487e7a 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -4,7 +4,7 @@ error: `std::string::String` may not be held across an `await` point per `clippy LL | let _x = String::from("hello"); | ^^ | - = note: strings are bad + = note: strings are bad (from clippy.toml) = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` @@ -19,7 +19,7 @@ error: `std::string::String` may not be held across an `await` point per `clippy LL | let _x = String::from("hi!"); | ^^ | - = note: strings are bad + = note: strings are bad (from clippy.toml) error: aborting due to 3 previous errors diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs index 22dcd3ae9d69..bff97d97df77 100644 --- a/tests/ui-toml/expect_used/expect_used.rs +++ b/tests/ui-toml/expect_used/expect_used.rs @@ -16,14 +16,19 @@ fn main() { expect_result(); } -#[test] -fn test_expect_option() { - let opt = Some(0); - let _ = opt.expect(""); -} +#[cfg(test)] +mod issue9612 { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + let _a: u8 = 2.try_into().unwrap(); + let _a: u8 = 3.try_into().expect(""); -#[test] -fn test_expect_result() { - let res: Result = Ok(0); - let _ = res.expect(""); + util(); + } + + fn util() { + let _a: u8 = 4.try_into().unwrap(); + let _a: u8 = 5.try_into().expect(""); + } } diff --git a/tests/ui-toml/expect_used/expect_used.stderr b/tests/ui-toml/expect_used/expect_used.stderr index 28a08599c67c..1e9bb48c333c 100644 --- a/tests/ui-toml/expect_used/expect_used.stderr +++ b/tests/ui-toml/expect_used/expect_used.stderr @@ -1,4 +1,4 @@ -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/expect_used.rs:6:13 | LL | let _ = opt.expect(""); @@ -7,7 +7,7 @@ LL | let _ = opt.expect(""); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/expect_used.rs:11:13 | LL | let _ = res.expect(""); diff --git a/tests/ui-toml/mut_key/clippy.toml b/tests/ui-toml/mut_key/clippy.toml new file mode 100644 index 000000000000..6d33e192ee89 --- /dev/null +++ b/tests/ui-toml/mut_key/clippy.toml @@ -0,0 +1 @@ +ignore-interior-mutability = ["mut_key::Counted"] \ No newline at end of file diff --git a/tests/ui-toml/mut_key/mut_key.rs b/tests/ui-toml/mut_key/mut_key.rs new file mode 100644 index 000000000000..667c51cb4a3f --- /dev/null +++ b/tests/ui-toml/mut_key/mut_key.rs @@ -0,0 +1,53 @@ +// compile-flags: --crate-name mut_key + +#![warn(clippy::mutable_key_type)] + +use std::cmp::{Eq, PartialEq}; +use std::collections::{HashMap, HashSet}; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; +use std::sync::atomic::{AtomicUsize, Ordering}; + +struct Counted { + count: AtomicUsize, + val: T, +} + +impl Clone for Counted { + fn clone(&self) -> Self { + Self { + count: AtomicUsize::new(0), + val: self.val.clone(), + } + } +} + +impl PartialEq for Counted { + fn eq(&self, other: &Self) -> bool { + self.val == other.val + } +} +impl Eq for Counted {} + +impl Hash for Counted { + fn hash(&self, state: &mut H) { + self.val.hash(state); + } +} + +impl Deref for Counted { + type Target = T; + + fn deref(&self) -> &T { + self.count.fetch_add(1, Ordering::AcqRel); + &self.val + } +} + +// This is not linted because `"mut_key::Counted"` is in +// `arc_like_types` in `clippy.toml` +fn should_not_take_this_arg(_v: HashSet>) {} + +fn main() { + should_not_take_this_arg(HashSet::new()); +} diff --git a/tests/ui-toml/print_macro/clippy.toml b/tests/ui-toml/print_macro/clippy.toml new file mode 100644 index 000000000000..40b1dda5b2ea --- /dev/null +++ b/tests/ui-toml/print_macro/clippy.toml @@ -0,0 +1 @@ +allow-print-in-tests = true diff --git a/tests/ui-toml/print_macro/print_macro.rs b/tests/ui-toml/print_macro/print_macro.rs new file mode 100644 index 000000000000..5aefb6a6b4d4 --- /dev/null +++ b/tests/ui-toml/print_macro/print_macro.rs @@ -0,0 +1,20 @@ +// compile-flags: --test +#![warn(clippy::print_stdout)] +#![warn(clippy::print_stderr)] + +fn foo(n: u32) { + print!("{n}"); + eprint!("{n}"); +} + +#[test] +pub fn foo1() { + print!("{}", 1); + eprint!("{}", 1); +} + +#[cfg(test)] +fn foo3() { + print!("{}", 1); + eprint!("{}", 1); +} diff --git a/tests/ui-toml/print_macro/print_macro.stderr b/tests/ui-toml/print_macro/print_macro.stderr new file mode 100644 index 000000000000..d4b1ae84fd7d --- /dev/null +++ b/tests/ui-toml/print_macro/print_macro.stderr @@ -0,0 +1,18 @@ +error: use of `print!` + --> $DIR/print_macro.rs:6:5 + | +LL | print!("{n}"); + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stdout` implied by `-D warnings` + +error: use of `eprint!` + --> $DIR/print_macro.rs:7:5 + | +LL | eprint!("{n}"); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stderr` implied by `-D warnings` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/toml_disallowed_methods/clippy.toml b/tests/ui-toml/toml_disallowed_methods/clippy.toml index 28774db625bb..41dbd5068479 100644 --- a/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -8,4 +8,10 @@ disallowed-methods = [ { path = "regex::Regex::is_match", reason = "no matching allowed" }, # can use an inline table but omit reason { path = "regex::Regex::new" }, + # local paths + "conf_disallowed_methods::local_fn", + "conf_disallowed_methods::local_mod::f", + "conf_disallowed_methods::Struct::method", + "conf_disallowed_methods::Trait::provided_method", + "conf_disallowed_methods::Trait::implemented_method", ] diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index b483f1600289..2f3160c83383 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -1,3 +1,5 @@ +// compile-flags: --crate-name conf_disallowed_methods + #![warn(clippy::disallowed_methods)] extern crate futures; @@ -6,6 +8,27 @@ extern crate regex; use futures::stream::{empty, select_all}; use regex::Regex; +fn local_fn() {} + +struct Struct; + +impl Struct { + fn method(&self) {} +} + +trait Trait { + fn provided_method(&self) {} + fn implemented_method(&self); +} + +impl Trait for Struct { + fn implemented_method(&self) {} +} + +mod local_mod { + pub fn f() {} +} + fn main() { let re = Regex::new(r"ab.*c").unwrap(); re.is_match("abc"); @@ -26,4 +49,11 @@ fn main() { // resolve ambiguity between `futures::stream::select_all` the module and the function let same_name_as_module = select_all(vec![empty::<()>()]); + + local_fn(); + local_mod::f(); + let s = Struct; + s.method(); + s.provided_method(); + s.implemented_method(); } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 6d78c32e127e..148d1cae51f1 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:10:14 + --> $DIR/conf_disallowed_methods.rs:33:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); = note: `-D clippy::disallowed-methods` implied by `-D warnings` error: use of a disallowed method `regex::Regex::is_match` - --> $DIR/conf_disallowed_methods.rs:11:5 + --> $DIR/conf_disallowed_methods.rs:34:5 | LL | re.is_match("abc"); | ^^^^^^^^^^^^^^^^^^ @@ -15,46 +15,76 @@ LL | re.is_match("abc"); = note: no matching allowed (from clippy.toml) error: use of a disallowed method `std::iter::Iterator::sum` - --> $DIR/conf_disallowed_methods.rs:14:5 + --> $DIR/conf_disallowed_methods.rs:37:5 | LL | a.iter().sum::(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `slice::sort_unstable` - --> $DIR/conf_disallowed_methods.rs:16:5 + --> $DIR/conf_disallowed_methods.rs:39:5 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:18:13 + --> $DIR/conf_disallowed_methods.rs:41:13 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:21:61 + --> $DIR/conf_disallowed_methods.rs:44:61 | LL | let indirect: fn(&str) -> Result = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:24:28 + --> $DIR/conf_disallowed_methods.rs:47:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:25:53 + --> $DIR/conf_disallowed_methods.rs:48:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ error: use of a disallowed method `futures::stream::select_all` - --> $DIR/conf_disallowed_methods.rs:28:31 + --> $DIR/conf_disallowed_methods.rs:51:31 | LL | let same_name_as_module = select_all(vec![empty::<()>()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: use of a disallowed method `conf_disallowed_methods::local_fn` + --> $DIR/conf_disallowed_methods.rs:53:5 + | +LL | local_fn(); + | ^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::local_mod::f` + --> $DIR/conf_disallowed_methods.rs:54:5 + | +LL | local_mod::f(); + | ^^^^^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::Struct::method` + --> $DIR/conf_disallowed_methods.rs:56:5 + | +LL | s.method(); + | ^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method` + --> $DIR/conf_disallowed_methods.rs:57:5 + | +LL | s.provided_method(); + | ^^^^^^^^^^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method` + --> $DIR/conf_disallowed_methods.rs:58:5 + | +LL | s.implemented_method(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 82ee80541321..f91d285c2e0e 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,6 +1,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of allow-dbg-in-tests allow-expect-in-tests + allow-print-in-tests allow-unwrap-in-tests allowed-scripts arithmetic-side-effects-allowed @@ -20,8 +21,10 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie enforced-import-renames enum-variant-name-threshold enum-variant-size-threshold + ignore-interior-mutability large-error-threshold literal-representation-threshold + matches-for-let-else max-fn-params-bools max-include-file-size max-struct-bools diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs index 0e82fb20e455..bc8e8c1f0703 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -66,8 +66,21 @@ fn main() { } } -#[test] -fn test() { - let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); - let _ = boxed_slice.get(1).unwrap(); +#[cfg(test)] +mod issue9612 { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + let _a: u8 = 2.try_into().unwrap(); + let _a: u8 = 3.try_into().expect(""); + + util(); + } + + fn util() { + let _a: u8 = 4.try_into().unwrap(); + let _a: u8 = 5.try_into().expect(""); + // should still warn + let _ = Box::new([0]).get(1).unwrap(); + } } diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr index 681b5eaf54db..94b5ef663add 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); @@ -25,7 +25,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); @@ -39,7 +39,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); @@ -53,7 +53,7 @@ error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); @@ -67,7 +67,7 @@ error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); @@ -81,7 +81,7 @@ error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); @@ -95,7 +95,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); @@ -109,7 +109,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; @@ -123,7 +123,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; @@ -137,7 +137,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; @@ -151,7 +151,7 @@ error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; @@ -165,7 +165,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); @@ -179,7 +179,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); @@ -188,10 +188,10 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:72:13 + --> $DIR/unwrap_used.rs:84:17 | -LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` +LL | let _ = Box::new([0]).get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&Box::new([0])[1]` error: aborting due to 27 previous errors diff --git a/tests/ui-toml/vec_box_sized/test.rs b/tests/ui-toml/vec_box_sized/test.rs index bf04bee16373..4c46deb585b6 100644 --- a/tests/ui-toml/vec_box_sized/test.rs +++ b/tests/ui-toml/vec_box_sized/test.rs @@ -7,8 +7,9 @@ struct C { } struct Foo(Vec>); -struct Bar(Vec>); -struct Baz(Vec>); +struct Bar(Vec>); +struct Quux(Vec>); +struct Baz(Vec>); struct BarBaz(Vec>); struct FooBarBaz(Vec>); diff --git a/tests/ui-toml/vec_box_sized/test.stderr b/tests/ui-toml/vec_box_sized/test.stderr index cf194de3c553..55de68f8ecf4 100644 --- a/tests/ui-toml/vec_box_sized/test.stderr +++ b/tests/ui-toml/vec_box_sized/test.stderr @@ -9,11 +9,11 @@ LL | struct Foo(Vec>); error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/test.rs:10:12 | -LL | struct Bar(Vec>); - | ^^^^^^^^^^^^^ help: try: `Vec` +LL | struct Bar(Vec>); + | ^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/test.rs:13:18 + --> $DIR/test.rs:14:18 | LL | struct FooBarBaz(Vec>); | ^^^^^^^^^^^ help: try: `Vec` diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index b25e68f13061..b5ed8988a518 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -150,8 +150,12 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n = 23 + 85; // Unary - _n = -1; - _n = -(-1); + _n = -2147483647; + _n = -i32::MAX; + _n = -i32::MIN; + _n = -&2147483647; + _n = -&i32::MAX; + _n = -&i32::MIN; } pub fn runtime_ops() { diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 0f06e22bae96..0259a0824e79 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -19,331 +19,331 @@ LL | let _ = inferred_string + ""; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:161:5 + --> $DIR/arithmetic_side_effects.rs:165:5 | LL | _n += 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:162:5 + --> $DIR/arithmetic_side_effects.rs:166:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:163:5 + --> $DIR/arithmetic_side_effects.rs:167:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:164:5 + --> $DIR/arithmetic_side_effects.rs:168:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:165:5 + --> $DIR/arithmetic_side_effects.rs:169:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:166:5 + --> $DIR/arithmetic_side_effects.rs:170:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:167:5 + --> $DIR/arithmetic_side_effects.rs:171:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:168:5 + --> $DIR/arithmetic_side_effects.rs:172:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:169:5 + --> $DIR/arithmetic_side_effects.rs:173:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:170:5 + --> $DIR/arithmetic_side_effects.rs:174:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:173:10 + --> $DIR/arithmetic_side_effects.rs:177:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:174:10 + --> $DIR/arithmetic_side_effects.rs:178:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:175:10 + --> $DIR/arithmetic_side_effects.rs:179:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:176:10 + --> $DIR/arithmetic_side_effects.rs:180:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:177:10 + --> $DIR/arithmetic_side_effects.rs:181:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:178:10 + --> $DIR/arithmetic_side_effects.rs:182:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:179:10 + --> $DIR/arithmetic_side_effects.rs:183:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:180:10 + --> $DIR/arithmetic_side_effects.rs:184:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:181:10 + --> $DIR/arithmetic_side_effects.rs:185:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:182:10 + --> $DIR/arithmetic_side_effects.rs:186:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:183:10 + --> $DIR/arithmetic_side_effects.rs:187:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:184:10 + --> $DIR/arithmetic_side_effects.rs:188:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:185:10 + --> $DIR/arithmetic_side_effects.rs:189:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:186:10 + --> $DIR/arithmetic_side_effects.rs:190:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:187:10 + --> $DIR/arithmetic_side_effects.rs:191:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:188:10 + --> $DIR/arithmetic_side_effects.rs:192:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:189:10 + --> $DIR/arithmetic_side_effects.rs:193:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:190:10 + --> $DIR/arithmetic_side_effects.rs:194:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:191:10 + --> $DIR/arithmetic_side_effects.rs:195:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:194:13 + --> $DIR/arithmetic_side_effects.rs:198:13 | LL | let _ = Custom + 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:195:13 + --> $DIR/arithmetic_side_effects.rs:199:13 | LL | let _ = Custom + 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:196:13 + --> $DIR/arithmetic_side_effects.rs:200:13 | LL | let _ = Custom + 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:197:13 + --> $DIR/arithmetic_side_effects.rs:201:13 | LL | let _ = Custom + 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:198:13 + --> $DIR/arithmetic_side_effects.rs:202:13 | LL | let _ = Custom + 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:199:13 + --> $DIR/arithmetic_side_effects.rs:203:13 | LL | let _ = Custom + 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:200:13 + --> $DIR/arithmetic_side_effects.rs:204:13 | LL | let _ = Custom - 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:201:13 + --> $DIR/arithmetic_side_effects.rs:205:13 | LL | let _ = Custom - 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:202:13 + --> $DIR/arithmetic_side_effects.rs:206:13 | LL | let _ = Custom - 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:203:13 + --> $DIR/arithmetic_side_effects.rs:207:13 | LL | let _ = Custom - 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:204:13 + --> $DIR/arithmetic_side_effects.rs:208:13 | LL | let _ = Custom - 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:205:13 + --> $DIR/arithmetic_side_effects.rs:209:13 | LL | let _ = Custom - 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:206:13 + --> $DIR/arithmetic_side_effects.rs:210:13 | LL | let _ = Custom / 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:207:13 + --> $DIR/arithmetic_side_effects.rs:211:13 | LL | let _ = Custom / 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:208:13 + --> $DIR/arithmetic_side_effects.rs:212:13 | LL | let _ = Custom / 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:209:13 + --> $DIR/arithmetic_side_effects.rs:213:13 | LL | let _ = Custom / 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:210:13 + --> $DIR/arithmetic_side_effects.rs:214:13 | LL | let _ = Custom / 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:211:13 + --> $DIR/arithmetic_side_effects.rs:215:13 | LL | let _ = Custom / 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:212:13 + --> $DIR/arithmetic_side_effects.rs:216:13 | LL | let _ = Custom * 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:213:13 + --> $DIR/arithmetic_side_effects.rs:217:13 | LL | let _ = Custom * 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:214:13 + --> $DIR/arithmetic_side_effects.rs:218:13 | LL | let _ = Custom * 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:215:13 + --> $DIR/arithmetic_side_effects.rs:219:13 | LL | let _ = Custom * 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:216:13 + --> $DIR/arithmetic_side_effects.rs:220:13 | LL | let _ = Custom * 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:217:13 + --> $DIR/arithmetic_side_effects.rs:221:13 | LL | let _ = Custom * 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:220:10 + --> $DIR/arithmetic_side_effects.rs:224:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:221:10 + --> $DIR/arithmetic_side_effects.rs:225:10 | LL | _n = -&_n; | ^^^^ diff --git a/tests/ui/auxiliary/doc_unsafe_macros.rs b/tests/ui/auxiliary/doc_unsafe_macros.rs index 869672d1eda5..3d917e3dc75e 100644 --- a/tests/ui/auxiliary/doc_unsafe_macros.rs +++ b/tests/ui/auxiliary/doc_unsafe_macros.rs @@ -6,3 +6,11 @@ macro_rules! undocd_unsafe { } }; } +#[macro_export] +macro_rules! undocd_safe { + () => { + pub fn vey_oy() { + unimplemented!(); + } + }; +} diff --git a/tests/ui/blanket_clippy_restriction_lints.rs b/tests/ui/blanket_clippy_restriction_lints.rs index d055f17526b9..554745368bca 100644 --- a/tests/ui/blanket_clippy_restriction_lints.rs +++ b/tests/ui/blanket_clippy_restriction_lints.rs @@ -1,3 +1,5 @@ +// compile-flags: -W clippy::restriction + #![warn(clippy::blanket_clippy_restriction_lints)] //! Test that the whole restriction group is not enabled diff --git a/tests/ui/blanket_clippy_restriction_lints.stderr b/tests/ui/blanket_clippy_restriction_lints.stderr index e83eb4d605aa..2bf89ab69a40 100644 --- a/tests/ui/blanket_clippy_restriction_lints.stderr +++ b/tests/ui/blanket_clippy_restriction_lints.stderr @@ -1,27 +1,32 @@ -error: restriction lints are not meant to be all enabled - --> $DIR/blanket_clippy_restriction_lints.rs:4:9 +error: `clippy::restriction` is not meant to be enabled as a group + | + = note: because of the command line `--warn clippy::restriction` + = help: enable the restriction lints you need individually + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + +error: `clippy::restriction` is not meant to be enabled as a group + --> $DIR/blanket_clippy_restriction_lints.rs:6:9 | LL | #![warn(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | - = help: try enabling only the lints you really need - = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + = help: enable the restriction lints you need individually -error: restriction lints are not meant to be all enabled - --> $DIR/blanket_clippy_restriction_lints.rs:5:9 +error: `clippy::restriction` is not meant to be enabled as a group + --> $DIR/blanket_clippy_restriction_lints.rs:7:9 | LL | #![deny(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | - = help: try enabling only the lints you really need + = help: enable the restriction lints you need individually -error: restriction lints are not meant to be all enabled - --> $DIR/blanket_clippy_restriction_lints.rs:6:11 +error: `clippy::restriction` is not meant to be enabled as a group + --> $DIR/blanket_clippy_restriction_lints.rs:8:11 | LL | #![forbid(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | - = help: try enabling only the lints you really need + = help: enable the restriction lints you need individually -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/bool_to_int_with_if.fixed b/tests/ui/bool_to_int_with_if.fixed index 2c8339cdd7f8..37d3e3286a4b 100644 --- a/tests/ui/bool_to_int_with_if.fixed +++ b/tests/ui/bool_to_int_with_if.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] @@ -76,6 +77,8 @@ fn main() { 123 }; + pub const SHOULD_NOT_LINT: usize = if true { 1 } else { 0 }; + some_fn(a); } @@ -89,3 +92,22 @@ fn side_effect() {} fn cond(a: bool, b: bool) -> bool { a || b } + +enum Enum { + A, + B, +} + +fn if_let(a: Enum, b: Enum) { + if let Enum::A = a { + 1 + } else { + 0 + }; + + if let Enum::A = a && let Enum::B = b { + 1 + } else { + 0 + }; +} diff --git a/tests/ui/bool_to_int_with_if.rs b/tests/ui/bool_to_int_with_if.rs index 5d9496f01775..ebdf86fd1856 100644 --- a/tests/ui/bool_to_int_with_if.rs +++ b/tests/ui/bool_to_int_with_if.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] @@ -108,6 +109,8 @@ fn main() { 123 }; + pub const SHOULD_NOT_LINT: usize = if true { 1 } else { 0 }; + some_fn(a); } @@ -121,3 +124,22 @@ fn side_effect() {} fn cond(a: bool, b: bool) -> bool { a || b } + +enum Enum { + A, + B, +} + +fn if_let(a: Enum, b: Enum) { + if let Enum::A = a { + 1 + } else { + 0 + }; + + if let Enum::A = a && let Enum::B = b { + 1 + } else { + 0 + }; +} diff --git a/tests/ui/bool_to_int_with_if.stderr b/tests/ui/bool_to_int_with_if.stderr index 4cb5531bef6a..5cfb75cc0dfc 100644 --- a/tests/ui/bool_to_int_with_if.stderr +++ b/tests/ui/bool_to_int_with_if.stderr @@ -1,5 +1,5 @@ error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:15:5 + --> $DIR/bool_to_int_with_if.rs:16:5 | LL | / if a { LL | | 1 @@ -12,7 +12,7 @@ LL | | }; = note: `-D clippy::bool-to-int-with-if` implied by `-D warnings` error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:20:5 + --> $DIR/bool_to_int_with_if.rs:21:5 | LL | / if a { LL | | 0 @@ -24,7 +24,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:25:5 + --> $DIR/bool_to_int_with_if.rs:26:5 | LL | / if !a { LL | | 1 @@ -36,7 +36,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:30:5 + --> $DIR/bool_to_int_with_if.rs:31:5 | LL | / if a || b { LL | | 1 @@ -48,7 +48,7 @@ LL | | }; = note: `(a || b) as i32` or `(a || b).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:35:5 + --> $DIR/bool_to_int_with_if.rs:36:5 | LL | / if cond(a, b) { LL | | 1 @@ -60,7 +60,7 @@ LL | | }; = note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:40:5 + --> $DIR/bool_to_int_with_if.rs:41:5 | LL | / if x + y < 4 { LL | | 1 @@ -72,7 +72,7 @@ LL | | }; = note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:49:12 + --> $DIR/bool_to_int_with_if.rs:50:12 | LL | } else if b { | ____________^ @@ -85,7 +85,7 @@ LL | | }; = note: `b as i32` or `b.into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:58:12 + --> $DIR/bool_to_int_with_if.rs:59:12 | LL | } else if b { | ____________^ @@ -98,7 +98,7 @@ LL | | }; = note: `!b as i32` or `(!b).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:116:5 + --> $DIR/bool_to_int_with_if.rs:119:5 | LL | if a { 1 } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)` diff --git a/tests/ui/cognitive_complexity.rs b/tests/ui/cognitive_complexity.rs index 912e6788afdd..07bdaff00dc4 100644 --- a/tests/ui/cognitive_complexity.rs +++ b/tests/ui/cognitive_complexity.rs @@ -393,3 +393,19 @@ impl Moo { } } } + +#[clippy::cognitive_complexity = "1"] +mod issue9300 { + async fn a() { + let a = 0; + if a == 0 {} + } + + pub struct S; + impl S { + pub async fn async_method() { + let a = 0; + if a == 0 {} + } + } +} diff --git a/tests/ui/cognitive_complexity.stderr b/tests/ui/cognitive_complexity.stderr index d7f2f24e52f2..5824631fa83b 100644 --- a/tests/ui/cognitive_complexity.stderr +++ b/tests/ui/cognitive_complexity.stderr @@ -135,5 +135,21 @@ LL | fn moo(&self) { | = help: you could split it up into multiple smaller functions -error: aborting due to 17 previous errors +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:399:14 + | +LL | async fn a() { + | ^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:406:22 + | +LL | pub async fn async_method() { + | ^^^^^^^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: aborting due to 19 previous errors diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr index 0c2d48f938fc..1f26c7f4db65 100644 --- a/tests/ui/crashes/ice-2774.stderr +++ b/tests/ui/crashes/ice-2774.stderr @@ -1,4 +1,4 @@ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a --> $DIR/ice-2774.rs:15:1 | LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { diff --git a/tests/ui/crashes/ice-9746.rs b/tests/ui/crashes/ice-9746.rs new file mode 100644 index 000000000000..fbd373c70bf2 --- /dev/null +++ b/tests/ui/crashes/ice-9746.rs @@ -0,0 +1,15 @@ +//! + +trait Trait {} + +struct Struct<'a> { + _inner: &'a Struct<'a>, +} + +impl Trait for Struct<'_> {} + +fn example<'a>(s: &'a Struct) -> Box> { + Box::new(Box::new(Struct { _inner: s })) +} + +fn main() {} diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr index d68bbe788021..875d5ab4f21c 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -1,4 +1,4 @@ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a --> $DIR/needless_lifetimes_impl_trait.rs:15:5 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { diff --git a/tests/ui/crate_level_checks/no_std_main_recursion.rs b/tests/ui/crate_level_checks/no_std_main_recursion.rs index 4a5c597dda51..e1c9fe30a9df 100644 --- a/tests/ui/crate_level_checks/no_std_main_recursion.rs +++ b/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -1,6 +1,5 @@ // compile-flags: -Clink-arg=-nostartfiles // ignore-macos -// ignore-windows #![feature(lang_items, start, libc)] #![no_std] diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index c7b616e28970..d74f2dbfe1ba 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -1,52 +1,40 @@ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:7:1 | -LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { -LL | | unimplemented!(); -LL | | } - | |_^ +LL | pub fn pub_fn_missing_errors_header() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::missing-errors-doc` implied by `-D warnings` error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:11:1 | -LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { -LL | | unimplemented!(); -LL | | } - | |_^ +LL | pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:16:1 | -LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> { -LL | | unimplemented!(); -LL | | } - | |_^ +LL | pub fn pub_fn_returning_io_result() -> io::Result<()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:21:1 | -LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { -LL | | unimplemented!(); -LL | | } - | |_^ +LL | pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:51:5 | -LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> { -LL | | unimplemented!(); -LL | | } - | |_____^ +LL | pub fn pub_method_missing_errors_header() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:56:5 | -LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { -LL | | unimplemented!(); -LL | | } - | |_____^ +LL | pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:85:5 diff --git a/tests/ui/doc_unnecessary_unsafe.rs b/tests/ui/doc_unnecessary_unsafe.rs new file mode 100644 index 000000000000..d9e9363b0f4b --- /dev/null +++ b/tests/ui/doc_unnecessary_unsafe.rs @@ -0,0 +1,148 @@ +// aux-build:doc_unsafe_macros.rs + +#![allow(clippy::let_unit_value)] + +#[macro_use] +extern crate doc_unsafe_macros; + +/// This is has no safety section, and does not need one either +pub fn destroy_the_planet() { + unimplemented!(); +} + +/// This one does not need a `Safety` section +/// +/// # Safety +/// +/// This function shouldn't be called unless the horsemen are ready +pub fn apocalypse(universe: &mut ()) { + unimplemented!(); +} + +/// This is a private function, skip to match behavior with `missing_safety_doc`. +/// +/// # Safety +/// +/// Boo! +fn you_dont_see_me() { + unimplemented!(); +} + +mod private_mod { + /// This is public but unexported function, skip to match behavior with `missing_safety_doc`. + /// + /// # Safety + /// + /// Very safe! + pub fn only_crate_wide_accessible() { + unimplemented!(); + } + + /// # Safety + /// + /// Unnecessary safety! + pub fn republished() { + unimplemented!(); + } +} + +pub use private_mod::republished; + +pub trait SafeTraitSafeMethods { + fn woefully_underdocumented(self); + + /// # Safety + /// + /// Unnecessary! + fn documented(self); +} + +pub trait SafeTrait { + fn method(); +} + +/// # Safety +/// +/// Unnecessary! +pub trait DocumentedSafeTrait { + fn method2(); +} + +pub struct Struct; + +impl SafeTraitSafeMethods for Struct { + fn woefully_underdocumented(self) { + // all is well + } + + fn documented(self) { + // all is still well + } +} + +impl SafeTrait for Struct { + fn method() {} +} + +impl DocumentedSafeTrait for Struct { + fn method2() {} +} + +impl Struct { + /// # Safety + /// + /// Unnecessary! + pub fn documented() -> Self { + unimplemented!(); + } + + pub fn undocumented(&self) { + unimplemented!(); + } + + /// Private, fine again to stay consistent with `missing_safety_doc`. + /// + /// # Safety + /// + /// Unnecessary! + fn private(&self) { + unimplemented!(); + } +} + +macro_rules! very_safe { + () => { + pub fn whee() { + unimplemented!() + } + + /// # Safety + /// + /// Driving is very safe already! + pub fn drive() { + whee() + } + }; +} + +very_safe!(); + +// we don't lint code from external macros +undocd_safe!(); + +fn main() {} + +// do not lint if any parent has `#[doc(hidden)]` attribute +// see #7347 +#[doc(hidden)] +pub mod __macro { + pub struct T; + impl T { + pub unsafe fn f() {} + } +} + +/// # Implementation safety +pub trait DocumentedSafeTraitWithImplementationHeader { + fn method(); +} diff --git a/tests/ui/doc_unnecessary_unsafe.stderr b/tests/ui/doc_unnecessary_unsafe.stderr new file mode 100644 index 000000000000..83b2efbb346b --- /dev/null +++ b/tests/ui/doc_unnecessary_unsafe.stderr @@ -0,0 +1,51 @@ +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:18:1 + | +LL | pub fn apocalypse(universe: &mut ()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-safety-doc` implied by `-D warnings` + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:44:5 + | +LL | pub fn republished() { + | ^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:57:5 + | +LL | fn documented(self); + | ^^^^^^^^^^^^^^^^^^^^ + +error: docs for safe trait have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:67:1 + | +LL | pub trait DocumentedSafeTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:95:5 + | +LL | pub fn documented() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:122:9 + | +LL | pub fn drive() { + | ^^^^^^^^^^^^^^ +... +LL | very_safe!(); + | ------------ in this macro invocation + | + = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for safe trait have unnecessary `# Safety` section + --> $DIR/doc_unnecessary_unsafe.rs:146:1 + | +LL | pub trait DocumentedSafeTraitWithImplementationHeader { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/doc_unsafe.stderr b/tests/ui/doc_unsafe.stderr index 904b88eaef62..a86e191370e3 100644 --- a/tests/ui/doc_unsafe.stderr +++ b/tests/ui/doc_unsafe.stderr @@ -1,20 +1,16 @@ error: unsafe function's docs miss `# Safety` section --> $DIR/doc_unsafe.rs:9:1 | -LL | / pub unsafe fn destroy_the_planet() { -LL | | unimplemented!(); -LL | | } - | |_^ +LL | pub unsafe fn destroy_the_planet() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::missing-safety-doc` implied by `-D warnings` error: unsafe function's docs miss `# Safety` section --> $DIR/doc_unsafe.rs:32:5 | -LL | / pub unsafe fn republished() { -LL | | unimplemented!(); -LL | | } - | |_____^ +LL | pub unsafe fn republished() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe function's docs miss `# Safety` section --> $DIR/doc_unsafe.rs:40:5 @@ -25,29 +21,23 @@ LL | unsafe fn woefully_underdocumented(self); error: docs for unsafe trait missing `# Safety` section --> $DIR/doc_unsafe.rs:46:1 | -LL | / pub unsafe trait UnsafeTrait { -LL | | fn method(); -LL | | } - | |_^ +LL | pub unsafe trait UnsafeTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe function's docs miss `# Safety` section --> $DIR/doc_unsafe.rs:76:5 | -LL | / pub unsafe fn more_undocumented_unsafe() -> Self { -LL | | unimplemented!(); -LL | | } - | |_____^ +LL | pub unsafe fn more_undocumented_unsafe() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe function's docs miss `# Safety` section --> $DIR/doc_unsafe.rs:92:9 | -LL | / pub unsafe fn whee() { -LL | | unimplemented!() -LL | | } - | |_________^ +LL | pub unsafe fn whee() { + | ^^^^^^^^^^^^^^^^^^^^ ... -LL | very_unsafe!(); - | -------------- in this macro invocation +LL | very_unsafe!(); + | -------------- in this macro invocation | = note: this error originates in the macro `very_unsafe` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 422f9486503d..e73795502652 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -2,6 +2,7 @@ #![warn(clippy::eq_op)] #![allow(clippy::double_parens, clippy::identity_op, clippy::nonminimal_bool)] +#![allow(clippy::suspicious_xor_used_as_pow)] fn main() { // simple values and comparisons diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 313ceed2b41f..d365ab27edc2 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -1,5 +1,5 @@ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:8:13 + --> $DIR/eq_op.rs:9:13 | LL | let _ = 1 == 1; | ^^^^^^ @@ -7,163 +7,163 @@ LL | let _ = 1 == 1; = note: `-D clippy::eq-op` implied by `-D warnings` error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:9:13 + --> $DIR/eq_op.rs:10:13 | LL | let _ = "no" == "no"; | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:11:13 + --> $DIR/eq_op.rs:12:13 | LL | let _ = false != false; | ^^^^^^^^^^^^^^ error: equal expressions as operands to `<` - --> $DIR/eq_op.rs:12:13 + --> $DIR/eq_op.rs:13:13 | LL | let _ = 1.5 < 1.5; | ^^^^^^^^^ error: equal expressions as operands to `>=` - --> $DIR/eq_op.rs:13:13 + --> $DIR/eq_op.rs:14:13 | LL | let _ = 1u64 >= 1u64; | ^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:16:13 + --> $DIR/eq_op.rs:17:13 | LL | let _ = (1u32 as u64) & (1u32 as u64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `^` - --> $DIR/eq_op.rs:19:17 + --> $DIR/eq_op.rs:20:17 | LL | let _ = 1 ^ ((((((1)))))); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `<` - --> $DIR/eq_op.rs:23:13 + --> $DIR/eq_op.rs:24:13 | LL | let _ = (-(2) < -(2)); | ^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:24:13 + --> $DIR/eq_op.rs:25:13 | LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:24:14 + --> $DIR/eq_op.rs:25:14 | LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:24:35 + --> $DIR/eq_op.rs:25:35 | LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:25:13 + --> $DIR/eq_op.rs:26:13 | LL | let _ = (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:28:13 + --> $DIR/eq_op.rs:29:13 | LL | let _ = ([1] != [1]); | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:29:13 + --> $DIR/eq_op.rs:30:13 | LL | let _ = ((1, 2) != (1, 2)); | ^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:33:13 + --> $DIR/eq_op.rs:34:13 | LL | let _ = 1 + 1 == 2; | ^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:34:13 + --> $DIR/eq_op.rs:35:13 | LL | let _ = 1 - 1 == 0; | ^^^^^^^^^^ error: equal expressions as operands to `-` - --> $DIR/eq_op.rs:34:13 + --> $DIR/eq_op.rs:35:13 | LL | let _ = 1 - 1 == 0; | ^^^^^ error: equal expressions as operands to `-` - --> $DIR/eq_op.rs:36:13 + --> $DIR/eq_op.rs:37:13 | LL | let _ = 1 - 1; | ^^^^^ error: equal expressions as operands to `/` - --> $DIR/eq_op.rs:37:13 + --> $DIR/eq_op.rs:38:13 | LL | let _ = 1 / 1; | ^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:38:13 + --> $DIR/eq_op.rs:39:13 | LL | let _ = true && true; | ^^^^^^^^^^^^ error: equal expressions as operands to `||` - --> $DIR/eq_op.rs:40:13 + --> $DIR/eq_op.rs:41:13 | LL | let _ = true || true; | ^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:45:13 + --> $DIR/eq_op.rs:46:13 | LL | let _ = a == b && b == a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:46:13 + --> $DIR/eq_op.rs:47:13 | LL | let _ = a != b && b != a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:47:13 + --> $DIR/eq_op.rs:48:13 | LL | let _ = a < b && b > a; | ^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:48:13 + --> $DIR/eq_op.rs:49:13 | LL | let _ = a <= b && b >= a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:51:13 + --> $DIR/eq_op.rs:52:13 | LL | let _ = a == a; | ^^^^^^ error: equal expressions as operands to `/` - --> $DIR/eq_op.rs:61:20 + --> $DIR/eq_op.rs:62:20 | LL | const D: u32 = A / A; | ^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:92:5 + --> $DIR/eq_op.rs:93:5 | LL | (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 687efdada6e3..9af2ba962720 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -23,6 +23,11 @@ struct Struct { b: bool, } +struct NoPartialEqStruct { + a: i32, + b: bool, +} + enum NotPartialEq { A, B, @@ -47,6 +52,7 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h = NoPartialEqStruct { a: 2, b: false }; // true @@ -66,10 +72,11 @@ fn main() { if let Some(3 | 4) = c {} if let Struct { a, b: false } = d {} if let Struct { a: 2, b: x } = d {} - if let NotPartialEq::A = f {} + if matches!(f, NotPartialEq::A) {} if g == NotStructuralEq::A {} - if let Some(NotPartialEq::A) = Some(f) {} + if matches!(Some(f), Some(NotPartialEq::A)) {} if Some(g) == Some(NotStructuralEq::A) {} + if matches!(h, NoPartialEqStruct { a: 2, b: false }) {} macro_rules! m1 { (x) => { diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 8c467d14d2a9..c3626c081dd5 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -23,6 +23,11 @@ struct Struct { b: bool, } +struct NoPartialEqStruct { + a: i32, + b: bool, +} + enum NotPartialEq { A, B, @@ -47,6 +52,7 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h = NoPartialEqStruct { a: 2, b: false }; // true @@ -70,6 +76,7 @@ fn main() { if let NotStructuralEq::A = g {} if let Some(NotPartialEq::A) = Some(f) {} if let Some(NotStructuralEq::A) = Some(g) {} + if let NoPartialEqStruct { a: 2, b: false } = h {} macro_rules! m1 { (x) => { diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index 9c4c3cc3682e..40ca75b8da22 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -1,5 +1,5 @@ error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:53:8 + --> $DIR/equatable_if_let.rs:59:8 | LL | if let 2 = a {} | ^^^^^^^^^ help: try: `a == 2` @@ -7,64 +7,82 @@ LL | if let 2 = a {} = note: `-D clippy::equatable-if-let` implied by `-D warnings` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:54:8 + --> $DIR/equatable_if_let.rs:60:8 | LL | if let Ordering::Greater = a.cmp(&b) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:55:8 + --> $DIR/equatable_if_let.rs:61:8 | LL | if let Some(2) = c {} | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:56:8 + --> $DIR/equatable_if_let.rs:62:8 | LL | if let Struct { a: 2, b: false } = d {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:57:8 + --> $DIR/equatable_if_let.rs:63:8 | LL | if let Enum::TupleVariant(32, 64) = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:58:8 + --> $DIR/equatable_if_let.rs:64:8 | LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:59:8 + --> $DIR/equatable_if_let.rs:65:8 | LL | if let Enum::UnitVariant = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:60:8 + --> $DIR/equatable_if_let.rs:66:8 | LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })` +error: this pattern matching can be expressed using `matches!` + --> $DIR/equatable_if_let.rs:75:8 + | +LL | if let NotPartialEq::A = f {} + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)` + error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:70:8 + --> $DIR/equatable_if_let.rs:76:8 | LL | if let NotStructuralEq::A = g {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A` +error: this pattern matching can be expressed using `matches!` + --> $DIR/equatable_if_let.rs:77:8 + | +LL | if let Some(NotPartialEq::A) = Some(f) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))` + error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:72:8 + --> $DIR/equatable_if_let.rs:78:8 | LL | if let Some(NotStructuralEq::A) = Some(g) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` -error: this pattern matching can be expressed using equality +error: this pattern matching can be expressed using `matches!` --> $DIR/equatable_if_let.rs:79:8 | +LL | if let NoPartialEqStruct { a: 2, b: false } = h {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:86:8 + | LL | if let m1!(x) = "abc" { | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)` -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/expect.stderr b/tests/ui/expect.stderr index f6738865cac1..c08e0dbbf744 100644 --- a/tests/ui/expect.stderr +++ b/tests/ui/expect.stderr @@ -1,4 +1,4 @@ -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/expect.rs:5:13 | LL | let _ = opt.expect(""); @@ -7,7 +7,7 @@ LL | let _ = opt.expect(""); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/expect.rs:10:13 | LL | let _ = res.expect(""); @@ -15,7 +15,7 @@ LL | let _ = res.expect(""); | = help: if this value is an `Err`, it will panic -error: used `expect_err()` on `a Result` value +error: used `expect_err()` on a `Result` value --> $DIR/expect.rs:11:13 | LL | let _ = res.expect_err(""); diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index d1d35e5c0eb4..59ff5e4040a3 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -266,4 +266,15 @@ fn main() { } x }; + + trait WithAssoc { + type Assoc: ?Sized; + } + impl WithAssoc for String { + type Assoc = str; + } + fn takes_assoc(_: &T::Assoc) -> T { + unimplemented!() + } + let _: String = takes_assoc(&*String::new()); } diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index deedafad153b..bcfb60c32788 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -266,4 +266,15 @@ fn main() { } *x }; + + trait WithAssoc { + type Assoc: ?Sized; + } + impl WithAssoc for String { + type Assoc = str; + } + fn takes_assoc(_: &T::Assoc) -> T { + unimplemented!() + } + let _: String = takes_assoc(&*String::new()); } diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index f805bcc9ba8a..f53e531629aa 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -2,6 +2,7 @@ #![allow(clippy::too_many_arguments)] extern "C" { + // Should not lint, most of the time users have no control over extern function signatures fn f(_: bool, _: bool, _: bool, _: bool); } @@ -22,8 +23,12 @@ fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} struct S; trait Trait { + // should warn for trait functions with and without body fn f(_: bool, _: bool, _: bool, _: bool); fn g(_: bool, _: bool, _: bool, _: Vec); + #[allow(clippy::fn_params_excessive_bools)] + fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool); + fn i(_: bool, _: bool, _: bool, _: bool) {} } impl S { @@ -34,8 +39,11 @@ impl S { } impl Trait for S { + // Should not lint because the trait might not be changeable by the user + // We only lint in the trait definition fn f(_: bool, _: bool, _: bool, _: bool) {} fn g(_: bool, _: bool, _: bool, _: Vec) {} + fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool) {} } fn main() { diff --git a/tests/ui/fn_params_excessive_bools.stderr b/tests/ui/fn_params_excessive_bools.stderr index 11627105691b..43363b46972c 100644 --- a/tests/ui/fn_params_excessive_bools.stderr +++ b/tests/ui/fn_params_excessive_bools.stderr @@ -1,5 +1,5 @@ error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:18:1 + --> $DIR/fn_params_excessive_bools.rs:19:1 | LL | fn g(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn g(_: bool, _: bool, _: bool, _: bool) {} = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings` error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:21:1 + --> $DIR/fn_params_excessive_bools.rs:22:1 | LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:25:5 + --> $DIR/fn_params_excessive_bools.rs:27:5 | LL | fn f(_: bool, _: bool, _: bool, _: bool); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,15 @@ LL | fn f(_: bool, _: bool, _: bool, _: bool); = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:30:5 + --> $DIR/fn_params_excessive_bools.rs:31:5 + | +LL | fn i(_: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:35:5 | LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +40,7 @@ LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:42:5 + --> $DIR/fn_params_excessive_bools.rs:50:5 | LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} @@ -42,12 +50,12 @@ LL | | } = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:43:9 + --> $DIR/fn_params_excessive_bools.rs:51:9 | LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider refactoring bools into two-variant enums -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/from_raw_with_void_ptr.rs b/tests/ui/from_raw_with_void_ptr.rs new file mode 100644 index 000000000000..8484da2415ab --- /dev/null +++ b/tests/ui/from_raw_with_void_ptr.rs @@ -0,0 +1,34 @@ +#![warn(clippy::from_raw_with_void_ptr)] + +use std::ffi::c_void; +use std::rc::Rc; +use std::sync::Arc; + +fn main() { + // must lint + let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void; + let _ = unsafe { Box::from_raw(ptr) }; + + // shouldn't be linted + let _ = unsafe { Box::from_raw(ptr as *mut usize) }; + + // shouldn't be linted + let should_not_lint_ptr = Box::into_raw(Box::new(12u8)) as *mut u8; + let _ = unsafe { Box::from_raw(should_not_lint_ptr as *mut u8) }; + + // must lint + let ptr = Rc::into_raw(Rc::new(42usize)) as *mut c_void; + let _ = unsafe { Rc::from_raw(ptr) }; + + // must lint + let ptr = Arc::into_raw(Arc::new(42usize)) as *mut c_void; + let _ = unsafe { Arc::from_raw(ptr) }; + + // must lint + let ptr = std::rc::Weak::into_raw(Rc::downgrade(&Rc::new(42usize))) as *mut c_void; + let _ = unsafe { std::rc::Weak::from_raw(ptr) }; + + // must lint + let ptr = std::sync::Weak::into_raw(Arc::downgrade(&Arc::new(42usize))) as *mut c_void; + let _ = unsafe { std::sync::Weak::from_raw(ptr) }; +} diff --git a/tests/ui/from_raw_with_void_ptr.stderr b/tests/ui/from_raw_with_void_ptr.stderr new file mode 100644 index 000000000000..96e4af12ba38 --- /dev/null +++ b/tests/ui/from_raw_with_void_ptr.stderr @@ -0,0 +1,63 @@ +error: creating a `Box` from a void raw pointer + --> $DIR/from_raw_with_void_ptr.rs:10:22 + | +LL | let _ = unsafe { Box::from_raw(ptr) }; + | ^^^^^^^^^^^^^^^^^^ + | +help: cast this to a pointer of the appropriate type + --> $DIR/from_raw_with_void_ptr.rs:10:36 + | +LL | let _ = unsafe { Box::from_raw(ptr) }; + | ^^^ + = note: `-D clippy::from-raw-with-void-ptr` implied by `-D warnings` + +error: creating a `Rc` from a void raw pointer + --> $DIR/from_raw_with_void_ptr.rs:21:22 + | +LL | let _ = unsafe { Rc::from_raw(ptr) }; + | ^^^^^^^^^^^^^^^^^ + | +help: cast this to a pointer of the appropriate type + --> $DIR/from_raw_with_void_ptr.rs:21:35 + | +LL | let _ = unsafe { Rc::from_raw(ptr) }; + | ^^^ + +error: creating a `Arc` from a void raw pointer + --> $DIR/from_raw_with_void_ptr.rs:25:22 + | +LL | let _ = unsafe { Arc::from_raw(ptr) }; + | ^^^^^^^^^^^^^^^^^^ + | +help: cast this to a pointer of the appropriate type + --> $DIR/from_raw_with_void_ptr.rs:25:36 + | +LL | let _ = unsafe { Arc::from_raw(ptr) }; + | ^^^ + +error: creating a `Weak` from a void raw pointer + --> $DIR/from_raw_with_void_ptr.rs:29:22 + | +LL | let _ = unsafe { std::rc::Weak::from_raw(ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: cast this to a pointer of the appropriate type + --> $DIR/from_raw_with_void_ptr.rs:29:46 + | +LL | let _ = unsafe { std::rc::Weak::from_raw(ptr) }; + | ^^^ + +error: creating a `Weak` from a void raw pointer + --> $DIR/from_raw_with_void_ptr.rs:33:22 + | +LL | let _ = unsafe { std::sync::Weak::from_raw(ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: cast this to a pointer of the appropriate type + --> $DIR/from_raw_with_void_ptr.rs:33:48 + | +LL | let _ = unsafe { std::sync::Weak::from_raw(ptr) }; + | ^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index 937f85904083..6dee4d5b4b62 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); @@ -25,7 +25,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); @@ -39,7 +39,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); @@ -53,7 +53,7 @@ error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); @@ -67,7 +67,7 @@ error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); @@ -81,7 +81,7 @@ error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); @@ -95,7 +95,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); @@ -109,7 +109,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; @@ -123,7 +123,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; @@ -137,7 +137,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; @@ -151,7 +151,7 @@ error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; @@ -165,7 +165,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); @@ -179,7 +179,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); diff --git a/tests/ui/infallible_destructuring_match.fixed b/tests/ui/infallible_destructuring_match.fixed index b8e40d995531..61985e56b769 100644 --- a/tests/ui/infallible_destructuring_match.fixed +++ b/tests/ui/infallible_destructuring_match.fixed @@ -9,6 +9,9 @@ enum SingleVariantEnum { struct TupleStruct(i32); +struct NonCopy; +struct TupleStructWithNonCopy(NonCopy); + enum EmptyEnum {} macro_rules! match_enum { @@ -71,6 +74,15 @@ fn infallible_destructuring_match_struct() { let TupleStruct(data) = wrapper; } +fn infallible_destructuring_match_struct_with_noncopy() { + let wrapper = TupleStructWithNonCopy(NonCopy); + + // This should lint! (keeping `ref` in the suggestion) + let TupleStructWithNonCopy(ref data) = wrapper; + + let TupleStructWithNonCopy(ref data) = wrapper; +} + macro_rules! match_never_enum { ($param:expr) => { let data = match $param { diff --git a/tests/ui/infallible_destructuring_match.rs b/tests/ui/infallible_destructuring_match.rs index 106cd438b90e..f2768245bbc4 100644 --- a/tests/ui/infallible_destructuring_match.rs +++ b/tests/ui/infallible_destructuring_match.rs @@ -9,6 +9,9 @@ enum SingleVariantEnum { struct TupleStruct(i32); +struct NonCopy; +struct TupleStructWithNonCopy(NonCopy); + enum EmptyEnum {} macro_rules! match_enum { @@ -75,6 +78,17 @@ fn infallible_destructuring_match_struct() { let TupleStruct(data) = wrapper; } +fn infallible_destructuring_match_struct_with_noncopy() { + let wrapper = TupleStructWithNonCopy(NonCopy); + + // This should lint! (keeping `ref` in the suggestion) + let data = match wrapper { + TupleStructWithNonCopy(ref n) => n, + }; + + let TupleStructWithNonCopy(ref data) = wrapper; +} + macro_rules! match_never_enum { ($param:expr) => { let data = match $param { diff --git a/tests/ui/infallible_destructuring_match.stderr b/tests/ui/infallible_destructuring_match.stderr index 1b78db42014a..f8a50f0223d6 100644 --- a/tests/ui/infallible_destructuring_match.stderr +++ b/tests/ui/infallible_destructuring_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:26:5 + --> $DIR/infallible_destructuring_match.rs:29:5 | LL | / let data = match wrapper { LL | | SingleVariantEnum::Variant(i) => i, @@ -9,7 +9,7 @@ LL | | }; = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings` error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:58:5 + --> $DIR/infallible_destructuring_match.rs:61:5 | LL | / let data = match wrapper { LL | | TupleStruct(i) => i, @@ -17,12 +17,20 @@ LL | | }; | |______^ help: try this: `let TupleStruct(data) = wrapper;` error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:90:5 + --> $DIR/infallible_destructuring_match.rs:85:5 + | +LL | / let data = match wrapper { +LL | | TupleStructWithNonCopy(ref n) => n, +LL | | }; + | |______^ help: try this: `let TupleStructWithNonCopy(ref data) = wrapper;` + +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` + --> $DIR/infallible_destructuring_match.rs:104:5 | LL | / let data = match wrapper { LL | | Ok(i) => i, LL | | }; | |______^ help: try this: `let Ok(data) = wrapper;` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index fb2a93c9580e..fd553aa4538a 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -1,4 +1,4 @@ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a --> $DIR/issue_4266.rs:4:1 | LL | async fn sink1<'a>(_: &'a str) {} // lint @@ -6,7 +6,7 @@ LL | async fn sink1<'a>(_: &'a str) {} // lint | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a --> $DIR/issue_4266.rs:8:1 | LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { diff --git a/tests/ui/let_underscore_drop.rs b/tests/ui/let_underscore_drop.rs deleted file mode 100644 index 11b50492ab29..000000000000 --- a/tests/ui/let_underscore_drop.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![warn(clippy::let_underscore_drop)] -#![allow(clippy::let_unit_value)] - -struct Droppable; - -impl Drop for Droppable { - fn drop(&mut self) {} -} - -fn main() { - let unit = (); - let boxed = Box::new(()); - let droppable = Droppable; - let optional = Some(Droppable); - - let _ = (); - let _ = Box::new(()); - let _ = Droppable; - let _ = Some(Droppable); - - // no lint for reference - let _ = droppable_ref(); -} - -#[must_use] -fn droppable_ref() -> &'static mut Droppable { - unimplemented!() -} diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr deleted file mode 100644 index 324b7cd431d4..000000000000 --- a/tests/ui/let_underscore_drop.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:17:5 - | -LL | let _ = Box::new(()); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - = note: `-D clippy::let-underscore-drop` implied by `-D warnings` - -error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:18:5 - | -LL | let _ = Droppable; - | ^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:19:5 - | -LL | let _ = Some(Droppable); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 3 previous errors - diff --git a/tests/ui/let_underscore_future.rs b/tests/ui/let_underscore_future.rs new file mode 100644 index 000000000000..d8f54cdca912 --- /dev/null +++ b/tests/ui/let_underscore_future.rs @@ -0,0 +1,20 @@ +use std::future::Future; + +async fn some_async_fn() {} + +fn sync_side_effects() {} +fn custom() -> impl Future { + sync_side_effects(); + async {} +} + +fn do_something_to_future(future: &mut impl Future) {} + +fn main() { + let _ = some_async_fn(); + let _ = custom(); + + let mut future = some_async_fn(); + do_something_to_future(&mut future); + let _ = future; +} diff --git a/tests/ui/let_underscore_future.stderr b/tests/ui/let_underscore_future.stderr new file mode 100644 index 000000000000..33a748736a88 --- /dev/null +++ b/tests/ui/let_underscore_future.stderr @@ -0,0 +1,27 @@ +error: non-binding `let` on a future + --> $DIR/let_underscore_future.rs:14:5 + | +LL | let _ = some_async_fn(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider awaiting the future or dropping explicitly with `std::mem::drop` + = note: `-D clippy::let-underscore-future` implied by `-D warnings` + +error: non-binding `let` on a future + --> $DIR/let_underscore_future.rs:15:5 + | +LL | let _ = custom(); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider awaiting the future or dropping explicitly with `std::mem::drop` + +error: non-binding `let` on a future + --> $DIR/let_underscore_future.rs:19:5 + | +LL | let _ = future; + | ^^^^^^^^^^^^^^^ + | + = help: consider awaiting the future or dropping explicitly with `std::mem::drop` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/let_underscore_lock.rs b/tests/ui/let_underscore_lock.rs index 7a7c4e924995..4dff4d766bcc 100644 --- a/tests/ui/let_underscore_lock.rs +++ b/tests/ui/let_underscore_lock.rs @@ -3,20 +3,6 @@ extern crate parking_lot; fn main() { - let m = std::sync::Mutex::new(()); - let rw = std::sync::RwLock::new(()); - - let _ = m.lock(); - let _ = rw.read(); - let _ = rw.write(); - let _ = m.try_lock(); - let _ = rw.try_read(); - let _ = rw.try_write(); - - // These shouldn't throw an error. - let _ = m; - let _ = rw; - use parking_lot::{lock_api::RawMutex, Mutex, RwLock}; let p_m: Mutex<()> = Mutex::const_new(RawMutex::INIT, ()); @@ -34,3 +20,20 @@ fn main() { let _ = p_m1; let _ = p_rw; } + +fn uplifted() { + // shouldn't lint std locks as they were uplifted as rustc's `let_underscore_lock` + + let m = std::sync::Mutex::new(()); + let rw = std::sync::RwLock::new(()); + + let _ = m.lock(); + let _ = rw.read(); + let _ = rw.write(); + let _ = m.try_lock(); + let _ = rw.try_read(); + let _ = rw.try_write(); + + let _ = m; + let _ = rw; +} diff --git a/tests/ui/let_underscore_lock.stderr b/tests/ui/let_underscore_lock.stderr index d7779e7b6c48..f137d4112092 100644 --- a/tests/ui/let_underscore_lock.stderr +++ b/tests/ui/let_underscore_lock.stderr @@ -1,83 +1,35 @@ -error: non-binding let on a synchronization lock +error: non-binding `let` on a synchronization lock --> $DIR/let_underscore_lock.rs:9:5 | -LL | let _ = m.lock(); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - = note: `-D clippy::let-underscore-lock` implied by `-D warnings` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:10:5 - | -LL | let _ = rw.read(); - | ^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:11:5 - | -LL | let _ = rw.write(); - | ^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:12:5 - | -LL | let _ = m.try_lock(); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:13:5 - | -LL | let _ = rw.try_read(); - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:14:5 - | -LL | let _ = rw.try_write(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:23:5 - | LL | let _ = p_m.lock(); | ^^^^^^^^^^^^^^^^^^^ | = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + = note: `-D clippy::let-underscore-lock` implied by `-D warnings` -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:26:5 +error: non-binding `let` on a synchronization lock + --> $DIR/let_underscore_lock.rs:12:5 | LL | let _ = p_m1.lock(); | ^^^^^^^^^^^^^^^^^^^^ | = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:29:5 +error: non-binding `let` on a synchronization lock + --> $DIR/let_underscore_lock.rs:15:5 | LL | let _ = p_rw.read(); | ^^^^^^^^^^^^^^^^^^^^ | = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:30:5 +error: non-binding `let` on a synchronization lock + --> $DIR/let_underscore_lock.rs:16:5 | LL | let _ = p_rw.write(); | ^^^^^^^^^^^^^^^^^^^^^ | = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` -error: aborting due to 10 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/let_underscore_must_use.stderr b/tests/ui/let_underscore_must_use.stderr index bae60f2ff9b7..28d760eb46ec 100644 --- a/tests/ui/let_underscore_must_use.stderr +++ b/tests/ui/let_underscore_must_use.stderr @@ -1,4 +1,4 @@ -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:67:5 | LL | let _ = f(); @@ -7,7 +7,7 @@ LL | let _ = f(); = help: consider explicitly using function result = note: `-D clippy::let-underscore-must-use` implied by `-D warnings` -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:68:5 | LL | let _ = g(); @@ -15,7 +15,7 @@ LL | let _ = g(); | = help: consider explicitly using expression value -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:70:5 | LL | let _ = l(0_u32); @@ -23,7 +23,7 @@ LL | let _ = l(0_u32); | = help: consider explicitly using function result -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:74:5 | LL | let _ = s.f(); @@ -31,7 +31,7 @@ LL | let _ = s.f(); | = help: consider explicitly using function result -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:75:5 | LL | let _ = s.g(); @@ -39,7 +39,7 @@ LL | let _ = s.g(); | = help: consider explicitly using expression value -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:78:5 | LL | let _ = S::h(); @@ -47,7 +47,7 @@ LL | let _ = S::h(); | = help: consider explicitly using function result -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:79:5 | LL | let _ = S::p(); @@ -55,7 +55,7 @@ LL | let _ = S::p(); | = help: consider explicitly using expression value -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:81:5 | LL | let _ = S::a(); @@ -63,7 +63,7 @@ LL | let _ = S::a(); | = help: consider explicitly using function result -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:83:5 | LL | let _ = if true { Ok(()) } else { Err(()) }; @@ -71,7 +71,7 @@ LL | let _ = if true { Ok(()) } else { Err(()) }; | = help: consider explicitly using expression value -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:87:5 | LL | let _ = a.is_ok(); @@ -79,7 +79,7 @@ LL | let _ = a.is_ok(); | = help: consider explicitly using function result -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:89:5 | LL | let _ = a.map(|_| ()); @@ -87,7 +87,7 @@ LL | let _ = a.map(|_| ()); | = help: consider explicitly using expression value -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:91:5 | LL | let _ = a; diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index 96cd87c0e19a..552213a7ff22 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -10,7 +10,7 @@ fn main() { } } - // Test for loop over implicitly implicitly adjusted `Iterator` with `if let` statement + // Test for loop over implicitly adjusted `Iterator` with `if let` statement let y: Vec> = vec![]; for n in y.clone() { if let Ok(n) = n { diff --git a/tests/ui/manual_instant_elapsed.fixed b/tests/ui/manual_instant_elapsed.fixed index 0fa776b7b2e4..85a91543c893 100644 --- a/tests/ui/manual_instant_elapsed.fixed +++ b/tests/ui/manual_instant_elapsed.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::unchecked_duration_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.rs b/tests/ui/manual_instant_elapsed.rs index 5b11b84535dd..c98cb15b9164 100644 --- a/tests/ui/manual_instant_elapsed.rs +++ b/tests/ui/manual_instant_elapsed.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::unchecked_duration_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.stderr b/tests/ui/manual_instant_elapsed.stderr index 5537f5642a23..4ce1f689107e 100644 --- a/tests/ui/manual_instant_elapsed.stderr +++ b/tests/ui/manual_instant_elapsed.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Instant::elapsed` - --> $DIR/manual_instant_elapsed.rs:17:20 + --> $DIR/manual_instant_elapsed.rs:18:20 | LL | let duration = Instant::now() - prev_instant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `prev_instant.elapsed()` @@ -7,7 +7,7 @@ LL | let duration = Instant::now() - prev_instant; = note: `-D clippy::manual-instant-elapsed` implied by `-D warnings` error: manual implementation of `Instant::elapsed` - --> $DIR/manual_instant_elapsed.rs:26:5 + --> $DIR/manual_instant_elapsed.rs:27:5 | LL | Instant::now() - *ref_to_instant; // to ensure parens are added correctly | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*ref_to_instant).elapsed()` diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed new file mode 100644 index 000000000000..765bb785994e --- /dev/null +++ b/tests/ui/manual_is_ascii_check.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![allow(unused, dead_code)] +#![warn(clippy::manual_is_ascii_check)] + +fn main() { + assert!('x'.is_ascii_lowercase()); + assert!('X'.is_ascii_uppercase()); + assert!(b'x'.is_ascii_lowercase()); + assert!(b'X'.is_ascii_uppercase()); + + let num = '2'; + assert!(num.is_ascii_digit()); + assert!(b'1'.is_ascii_digit()); + assert!('x'.is_ascii_alphabetic()); + + assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); +} + +fn msrv_1_23() { + #![clippy::msrv = "1.23"] + + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +fn msrv_1_24() { + #![clippy::msrv = "1.24"] + + assert!(b'1'.is_ascii_digit()); + assert!('X'.is_ascii_uppercase()); + assert!('x'.is_ascii_alphabetic()); +} + +fn msrv_1_46() { + #![clippy::msrv = "1.46"] + const FOO: bool = matches!('x', '0'..='9'); +} + +fn msrv_1_47() { + #![clippy::msrv = "1.47"] + const FOO: bool = 'x'.is_ascii_digit(); +} diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs new file mode 100644 index 000000000000..be1331610412 --- /dev/null +++ b/tests/ui/manual_is_ascii_check.rs @@ -0,0 +1,45 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![allow(unused, dead_code)] +#![warn(clippy::manual_is_ascii_check)] + +fn main() { + assert!(matches!('x', 'a'..='z')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!(b'x', b'a'..=b'z')); + assert!(matches!(b'X', b'A'..=b'Z')); + + let num = '2'; + assert!(matches!(num, '0'..='9')); + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + + assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); +} + +fn msrv_1_23() { + #![clippy::msrv = "1.23"] + + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +fn msrv_1_24() { + #![clippy::msrv = "1.24"] + + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +fn msrv_1_46() { + #![clippy::msrv = "1.46"] + const FOO: bool = matches!('x', '0'..='9'); +} + +fn msrv_1_47() { + #![clippy::msrv = "1.47"] + const FOO: bool = matches!('x', '0'..='9'); +} diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr new file mode 100644 index 000000000000..c0a9d4db1a15 --- /dev/null +++ b/tests/ui/manual_is_ascii_check.stderr @@ -0,0 +1,70 @@ +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:8:13 + | +LL | assert!(matches!('x', 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_lowercase()` + | + = note: `-D clippy::manual-is-ascii-check` implied by `-D warnings` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:9:13 + | +LL | assert!(matches!('X', 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:10:13 + | +LL | assert!(matches!(b'x', b'a'..=b'z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'x'.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:11:13 + | +LL | assert!(matches!(b'X', b'A'..=b'Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:14:13 + | +LL | assert!(matches!(num, '0'..='9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:15:13 + | +LL | assert!(matches!(b'1', b'0'..=b'9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:16:13 + | +LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:32:13 + | +LL | assert!(matches!(b'1', b'0'..=b'9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:33:13 + | +LL | assert!(matches!('X', 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:34:13 + | +LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:44:23 + | +LL | const FOO: bool = matches!('x', '0'..='9'); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` + +error: aborting due to 11 previous errors + diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs new file mode 100644 index 000000000000..2ef40e5911af --- /dev/null +++ b/tests/ui/manual_let_else.rs @@ -0,0 +1,237 @@ +#![allow(unused_braces, unused_variables, dead_code)] +#![allow( + clippy::collapsible_else_if, + clippy::unused_unit, + clippy::let_unit_value, + clippy::match_single_binding, + clippy::never_loop +)] +#![warn(clippy::manual_let_else)] + +fn g() -> Option<()> { + None +} + +fn main() {} + +fn fire() { + let v = if let Some(v_some) = g() { v_some } else { return }; + let v = if let Some(v_some) = g() { + v_some + } else { + return; + }; + + let v = if let Some(v) = g() { + // Blocks around the identity should have no impact + { + { v } + } + } else { + // Some computation should still make it fire + g(); + return; + }; + + // continue and break diverge + loop { + let v = if let Some(v_some) = g() { v_some } else { continue }; + let v = if let Some(v_some) = g() { v_some } else { break }; + } + + // panic also diverges + let v = if let Some(v_some) = g() { v_some } else { panic!() }; + + // abort also diverges + let v = if let Some(v_some) = g() { + v_some + } else { + std::process::abort() + }; + + // If whose two branches diverge also diverges + let v = if let Some(v_some) = g() { + v_some + } else { + if true { return } else { panic!() } + }; + + // Diverging after an if still makes the block diverge: + let v = if let Some(v_some) = g() { + v_some + } else { + if true {} + panic!(); + }; + + // A match diverges if all branches diverge: + // Note: the corresponding let-else requires a ; at the end of the match + // as otherwise the type checker does not turn it into a ! type. + let v = if let Some(v_some) = g() { + v_some + } else { + match () { + _ if panic!() => {}, + _ => panic!(), + } + }; + + // An if's expression can cause divergence: + let v = if let Some(v_some) = g() { v_some } else { if panic!() {} }; + + // An expression of a match can cause divergence: + let v = if let Some(v_some) = g() { + v_some + } else { + match panic!() { + _ => {}, + } + }; + + // Top level else if + let v = if let Some(v_some) = g() { + v_some + } else if true { + return; + } else { + panic!("diverge"); + }; + + // All match arms diverge + let v = if let Some(v_some) = g() { + v_some + } else { + match (g(), g()) { + (Some(_), None) => return, + (None, Some(_)) => { + if true { + return; + } else { + panic!(); + } + }, + _ => return, + } + }; + + // Tuples supported for the declared variables + let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) { + v_some + } else { + return; + }; + + // Tuples supported for the identity block and pattern + let v = if let (Some(v_some), w_some) = (g(), 0) { + (w_some, v_some) + } else { + return; + }; + + // entirely inside macro lints + macro_rules! create_binding_if_some { + ($n:ident, $e:expr) => { + let $n = if let Some(v) = $e { v } else { return }; + }; + } + create_binding_if_some!(w, g()); +} + +fn not_fire() { + let v = if let Some(v_some) = g() { + // Nothing returned. Should not fire. + } else { + return; + }; + + let w = 0; + let v = if let Some(v_some) = g() { + // Different variable than v_some. Should not fire. + w + } else { + return; + }; + + let v = if let Some(v_some) = g() { + // Computation in then clause. Should not fire. + g(); + v_some + } else { + return; + }; + + let v = if let Some(v_some) = g() { + v_some + } else { + if false { + return; + } + // This doesn't diverge. Should not fire. + () + }; + + let v = if let Some(v_some) = g() { + v_some + } else { + // There is one match arm that doesn't diverge. Should not fire. + match (g(), g()) { + (Some(_), None) => return, + (None, Some(_)) => return, + (Some(_), Some(_)) => (), + _ => return, + } + }; + + let v = if let Some(v_some) = g() { + v_some + } else { + // loop with a break statement inside does not diverge. + loop { + break; + } + }; + + enum Uninhabited {} + fn un() -> Uninhabited { + panic!() + } + let v = if let Some(v_some) = None { + v_some + } else { + // Don't lint if the type is uninhabited but not ! + un() + }; + + fn question_mark() -> Option<()> { + let v = if let Some(v) = g() { + v + } else { + // Question mark does not diverge + g()? + }; + Some(v) + } + + // Macro boundary inside let + macro_rules! some_or_return { + ($e:expr) => { + if let Some(v) = $e { v } else { return } + }; + } + let v = some_or_return!(g()); + + // Also macro boundary inside let, but inside a macro + macro_rules! create_binding_if_some_nf { + ($n:ident, $e:expr) => { + let $n = some_or_return!($e); + }; + } + create_binding_if_some_nf!(v, g()); + + // Already a let-else + let Some(a) = (if let Some(b) = Some(Some(())) { b } else { return }) else { panic!() }; + + // If a type annotation is present, don't lint as + // expressing the type might be too hard + let v: () = if let Some(v_some) = g() { v_some } else { panic!() }; +} diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr new file mode 100644 index 000000000000..453b68b8bd00 --- /dev/null +++ b/tests/ui/manual_let_else.stderr @@ -0,0 +1,263 @@ +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:18:5 + | +LL | let v = if let Some(v_some) = g() { v_some } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { return };` + | + = note: `-D clippy::manual-let-else` implied by `-D warnings` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:19:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | return; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:25:5 + | +LL | / let v = if let Some(v) = g() { +LL | | // Blocks around the identity should have no impact +LL | | { +LL | | { v } +... | +LL | | return; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v) = g() else { +LL + // Some computation should still make it fire +LL + g(); +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:38:9 + | +LL | let v = if let Some(v_some) = g() { v_some } else { continue }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { continue };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:39:9 + | +LL | let v = if let Some(v_some) = g() { v_some } else { break }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { break };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:43:5 + | +LL | let v = if let Some(v_some) = g() { v_some } else { panic!() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { panic!() };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:46:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | std::process::abort() +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + std::process::abort() +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:53:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | if true { return } else { panic!() } +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + if true { return } else { panic!() } +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:60:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | if true {} +LL | | panic!(); +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + if true {} +LL + panic!(); +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:70:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | match () { +... | +LL | | } +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + match () { +LL + _ if panic!() => {}, +LL + _ => panic!(), +LL + } +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:80:5 + | +LL | let v = if let Some(v_some) = g() { v_some } else { if panic!() {} }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { if panic!() {} };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:83:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | match panic!() { +LL | | _ => {}, +LL | | } +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + match panic!() { +LL + _ => {}, +LL + } +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:92:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else if true { +LL | | return; +LL | | } else { +LL | | panic!("diverge"); +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { if true { +LL + return; +LL + } else { +LL + panic!("diverge"); +LL + } }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:101:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | match (g(), g()) { +... | +LL | | } +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + match (g(), g()) { +LL + (Some(_), None) => return, +LL + (None, Some(_)) => { +LL + if true { +LL + return; +LL + } else { +LL + panic!(); +LL + } +LL + }, +LL + _ => return, +LL + } +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:118:5 + | +LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) { +LL | | v_some +LL | | } else { +LL | | return; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g().map(|v| (v, 42)) else { +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:125:5 + | +LL | / let v = if let (Some(v_some), w_some) = (g(), 0) { +LL | | (w_some, v_some) +LL | | } else { +LL | | return; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let (Some(v_some), w_some) = (g(), 0) else { +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:134:13 + | +LL | let $n = if let Some(v) = $e { v } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` +... +LL | create_binding_if_some!(w, g()); + | ------------------------------- in this macro invocation + | + = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 17 previous errors + diff --git a/tests/ui/manual_let_else_match.rs b/tests/ui/manual_let_else_match.rs new file mode 100644 index 000000000000..93c86ca24fea --- /dev/null +++ b/tests/ui/manual_let_else_match.rs @@ -0,0 +1,121 @@ +#![allow(unused_braces, unused_variables, dead_code)] +#![allow(clippy::collapsible_else_if, clippy::let_unit_value)] +#![warn(clippy::manual_let_else)] +// Ensure that we don't conflict with match -> if let lints +#![warn(clippy::single_match_else, clippy::single_match)] + +fn f() -> Result { + Ok(0) +} + +fn g() -> Option<()> { + None +} + +fn h() -> (Option<()>, Option<()>) { + (None, None) +} + +enum Variant { + Foo, + Bar(u32), + Baz(u32), +} + +fn build_enum() -> Variant { + Variant::Foo +} + +fn main() {} + +fn fire() { + let v = match g() { + Some(v_some) => v_some, + None => return, + }; + + let v = match g() { + Some(v_some) => v_some, + _ => return, + }; + + loop { + // More complex pattern for the identity arm and diverging arm + let v = match h() { + (Some(_), Some(_)) | (None, None) => continue, + (Some(v), None) | (None, Some(v)) => v, + }; + // Custom enums are supported as long as the "else" arm is a simple _ + let v = match build_enum() { + _ => continue, + Variant::Bar(v) | Variant::Baz(v) => v, + }; + } + + // There is a _ in the diverging arm + // TODO also support unused bindings aka _v + let v = match f() { + Ok(v) => v, + Err(_) => return, + }; + + // Err(()) is an allowed pattern + let v = match f().map_err(|_| ()) { + Ok(v) => v, + Err(()) => return, + }; +} + +fn not_fire() { + // Multiple diverging arms + let v = match h() { + _ => panic!(), + (None, Some(_v)) => return, + (Some(v), None) => v, + }; + + // Multiple identity arms + let v = match h() { + _ => panic!(), + (None, Some(v)) => v, + (Some(v), None) => v, + }; + + // No diverging arm at all, only identity arms. + // This is no case for let else, but destructuring assignment. + let v = match f() { + Ok(v) => v, + Err(e) => e, + }; + + // The identity arm has a guard + let v = match g() { + Some(v) if g().is_none() => v, + _ => return, + }; + + // The diverging arm has a guard + let v = match f() { + Err(v) if v > 0 => panic!(), + Ok(v) | Err(v) => v, + }; + + // The diverging arm creates a binding + let v = match f() { + Ok(v) => v, + Err(e) => panic!("error: {e}"), + }; + + // Custom enum where the diverging arm + // explicitly mentions the variant + let v = match build_enum() { + Variant::Foo => return, + Variant::Bar(v) | Variant::Baz(v) => v, + }; + + // The custom enum is surrounded by an Err() + let v = match Err(build_enum()) { + Ok(v) | Err(Variant::Bar(v) | Variant::Baz(v)) => v, + Err(Variant::Foo) => return, + }; +} diff --git a/tests/ui/manual_let_else_match.stderr b/tests/ui/manual_let_else_match.stderr new file mode 100644 index 000000000000..38be5ac54547 --- /dev/null +++ b/tests/ui/manual_let_else_match.stderr @@ -0,0 +1,58 @@ +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:32:5 + | +LL | / let v = match g() { +LL | | Some(v_some) => v_some, +LL | | None => return, +LL | | }; + | |______^ help: consider writing: `let Some(v_some) = g() else { return };` + | + = note: `-D clippy::manual-let-else` implied by `-D warnings` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:37:5 + | +LL | / let v = match g() { +LL | | Some(v_some) => v_some, +LL | | _ => return, +LL | | }; + | |______^ help: consider writing: `let Some(v_some) = g() else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:44:9 + | +LL | / let v = match h() { +LL | | (Some(_), Some(_)) | (None, None) => continue, +LL | | (Some(v), None) | (None, Some(v)) => v, +LL | | }; + | |__________^ help: consider writing: `let (Some(v), None) | (None, Some(v)) = h() else { continue };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:49:9 + | +LL | / let v = match build_enum() { +LL | | _ => continue, +LL | | Variant::Bar(v) | Variant::Baz(v) => v, +LL | | }; + | |__________^ help: consider writing: `let Variant::Bar(v) | Variant::Baz(v) = build_enum() else { continue };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:57:5 + | +LL | / let v = match f() { +LL | | Ok(v) => v, +LL | | Err(_) => return, +LL | | }; + | |______^ help: consider writing: `let Ok(v) = f() else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:63:5 + | +LL | / let v = match f().map_err(|_| ()) { +LL | | Ok(v) => v, +LL | | Err(()) => return, +LL | | }; + | |______^ help: consider writing: `let Ok(v) = f().map_err(|_| ()) else { return };` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/manual_ok_or.fixed b/tests/ui/manual_ok_or.fixed index d864f8554534..fc8511626b3d 100644 --- a/tests/ui/manual_ok_or.fixed +++ b/tests/ui/manual_ok_or.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::manual_ok_or)] +#![allow(clippy::or_fun_call)] #![allow(clippy::disallowed_names)] #![allow(clippy::redundant_closure)] #![allow(dead_code)] diff --git a/tests/ui/manual_ok_or.rs b/tests/ui/manual_ok_or.rs index 6264768460ef..b5303d33f5fd 100644 --- a/tests/ui/manual_ok_or.rs +++ b/tests/ui/manual_ok_or.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::manual_ok_or)] +#![allow(clippy::or_fun_call)] #![allow(clippy::disallowed_names)] #![allow(clippy::redundant_closure)] #![allow(dead_code)] diff --git a/tests/ui/manual_ok_or.stderr b/tests/ui/manual_ok_or.stderr index 65459a097384..b4a17f143e3f 100644 --- a/tests/ui/manual_ok_or.stderr +++ b/tests/ui/manual_ok_or.stderr @@ -1,5 +1,5 @@ error: this pattern reimplements `Option::ok_or` - --> $DIR/manual_ok_or.rs:11:5 + --> $DIR/manual_ok_or.rs:12:5 | LL | foo.map_or(Err("error"), |v| Ok(v)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")` @@ -7,19 +7,19 @@ LL | foo.map_or(Err("error"), |v| Ok(v)); = note: `-D clippy::manual-ok-or` implied by `-D warnings` error: this pattern reimplements `Option::ok_or` - --> $DIR/manual_ok_or.rs:14:5 + --> $DIR/manual_ok_or.rs:15:5 | LL | foo.map_or(Err("error"), Ok); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")` error: this pattern reimplements `Option::ok_or` - --> $DIR/manual_ok_or.rs:17:5 + --> $DIR/manual_ok_or.rs:18:5 | LL | None::.map_or(Err("error"), |v| Ok(v)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `None::.ok_or("error")` error: this pattern reimplements `Option::ok_or` - --> $DIR/manual_ok_or.rs:21:5 + --> $DIR/manual_ok_or.rs:22:5 | LL | / foo.map_or(Err::( LL | | &format!( diff --git a/tests/ui/map_flatten_fixable.fixed b/tests/ui/map_flatten_fixable.fixed index 312819a0a2cf..53628ef6531a 100644 --- a/tests/ui/map_flatten_fixable.fixed +++ b/tests/ui/map_flatten_fixable.fixed @@ -1,7 +1,6 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] #![allow(clippy::redundant_closure)] diff --git a/tests/ui/map_flatten_fixable.rs b/tests/ui/map_flatten_fixable.rs index 3fbf4f9a1b04..76016c8ed3cd 100644 --- a/tests/ui/map_flatten_fixable.rs +++ b/tests/ui/map_flatten_fixable.rs @@ -1,7 +1,6 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] #![allow(clippy::redundant_closure)] diff --git a/tests/ui/map_flatten_fixable.stderr b/tests/ui/map_flatten_fixable.stderr index c91f0b9ae94f..b6b0c4d09c37 100644 --- a/tests/ui/map_flatten_fixable.stderr +++ b/tests/ui/map_flatten_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:18:47 + --> $DIR/map_flatten_fixable.rs:17:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id)` @@ -7,43 +7,43 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll = note: `-D clippy::map-flatten` implied by `-D warnings` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:19:47 + --> $DIR/map_flatten_fixable.rs:18:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_ref)` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:20:47 + --> $DIR/map_flatten_fixable.rs:19:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_closure)` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:21:47 + --> $DIR/map_flatten_fixable.rs:20:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(|x| x.checked_add(1))` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:24:47 + --> $DIR/map_flatten_fixable.rs:23:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| 0..x)` error: called `map(..).flatten()` on `Option` - --> $DIR/map_flatten_fixable.rs:27:40 + --> $DIR/map_flatten_fixable.rs:26:40 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Result` - --> $DIR/map_flatten_fixable.rs:30:42 + --> $DIR/map_flatten_fixable.rs:29:42 | LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:39:10 + --> $DIR/map_flatten_fixable.rs:38:10 | LL | .map(|n| match n { | __________^ @@ -72,7 +72,7 @@ LL ~ }); | error: called `map(..).flatten()` on `Option` - --> $DIR/map_flatten_fixable.rs:59:10 + --> $DIR/map_flatten_fixable.rs:58:10 | LL | .map(|_| { | __________^ diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 2498007694c5..968f462f8a02 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -2,7 +2,12 @@ #![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] +#![allow( + unreachable_patterns, + dead_code, + clippy::equatable_if_let, + clippy::needless_borrowed_reference +)] fn main() { let x = Some(5); diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index b4e48499bd0f..c6b479e27c5a 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -2,7 +2,12 @@ #![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] +#![allow( + unreachable_patterns, + dead_code, + clippy::equatable_if_let, + clippy::needless_borrowed_reference +)] fn main() { let x = Some(5); diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index f1d1c23aeb0d..a4df8008ac23 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:11:14 + --> $DIR/match_expr_like_matches_macro.rs:16:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:17:14 + --> $DIR/match_expr_like_matches_macro.rs:22:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:23:14 + --> $DIR/match_expr_like_matches_macro.rs:28:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:29:15 + --> $DIR/match_expr_like_matches_macro.rs:34:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:35:16 + --> $DIR/match_expr_like_matches_macro.rs:40:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:59:20 + --> $DIR/match_expr_like_matches_macro.rs:64:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:69:20 + --> $DIR/match_expr_like_matches_macro.rs:74:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:79:20 + --> $DIR/match_expr_like_matches_macro.rs:84:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:139:18 + --> $DIR/match_expr_like_matches_macro.rs:144:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:148:18 + --> $DIR/match_expr_like_matches_macro.rs:153:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:165:21 + --> $DIR/match_expr_like_matches_macro.rs:170:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:179:20 + --> $DIR/match_expr_like_matches_macro.rs:184:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:191:20 + --> $DIR/match_expr_like_matches_macro.rs:196:20 | LL | let _res = match &val { | ____________________^ @@ -134,7 +134,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:251:14 + --> $DIR/match_expr_like_matches_macro.rs:256:14 | LL | let _y = match Some(5) { | ______________^ diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr index c9ded7f1ad03..183c262ce0b5 100644 --- a/tests/ui/missing_panics_doc.stderr +++ b/tests/ui/missing_panics_doc.stderr @@ -1,11 +1,8 @@ error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:6:1 | -LL | / pub fn unwrap() { -LL | | let result = Err("Hi"); -LL | | result.unwrap() -LL | | } - | |_^ +LL | pub fn unwrap() { + | ^^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:8:5 @@ -17,10 +14,8 @@ LL | result.unwrap() error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:12:1 | -LL | / pub fn panic() { -LL | | panic!("This function panics") -LL | | } - | |_^ +LL | pub fn panic() { + | ^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:13:5 @@ -31,10 +26,8 @@ LL | panic!("This function panics") error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:17:1 | -LL | / pub fn todo() { -LL | | todo!() -LL | | } - | |_^ +LL | pub fn todo() { + | ^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:18:5 @@ -45,14 +38,8 @@ LL | todo!() error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:22:1 | -LL | / pub fn inner_body(opt: Option) { -LL | | opt.map(|x| { -LL | | if x == 10 { -LL | | panic!() -LL | | } -LL | | }); -LL | | } - | |_^ +LL | pub fn inner_body(opt: Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:25:13 @@ -63,10 +50,8 @@ LL | panic!() error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:31:1 | -LL | / pub fn unreachable_and_panic() { -LL | | if true { unreachable!() } else { panic!() } -LL | | } - | |_^ +LL | pub fn unreachable_and_panic() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:32:39 @@ -77,11 +62,8 @@ LL | if true { unreachable!() } else { panic!() } error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:36:1 | -LL | / pub fn assert_eq() { -LL | | let x = 0; -LL | | assert_eq!(x, 0); -LL | | } - | |_^ +LL | pub fn assert_eq() { + | ^^^^^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:38:5 @@ -92,11 +74,8 @@ LL | assert_eq!(x, 0); error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:42:1 | -LL | / pub fn assert_ne() { -LL | | let x = 0; -LL | | assert_ne!(x, 0); -LL | | } - | |_^ +LL | pub fn assert_ne() { + | ^^^^^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:44:5 diff --git a/tests/ui/mut_from_ref.rs b/tests/ui/mut_from_ref.rs index 370dbd588216..7de153305947 100644 --- a/tests/ui/mut_from_ref.rs +++ b/tests/ui/mut_from_ref.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![allow(unused, clippy::needless_lifetimes)] #![warn(clippy::mut_from_ref)] struct Foo; diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index ac8fd9d8fb09..ee3a856566cc 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -57,3 +57,20 @@ fn issue6922() { // do not lint from an external macro mut_mut!(); } + +mod issue9035 { + use std::fmt::Display; + + struct Foo<'a> { + inner: &'a mut dyn Display, + } + + impl Foo<'_> { + fn foo(&mut self) { + let hlp = &mut self.inner; + bar(hlp); + } + } + + fn bar(_: &mut impl Display) {} +} diff --git a/tests/ui/mut_range_bound.rs b/tests/ui/mut_range_bound.rs index e1ae1ef92822..7fdeb27ed988 100644 --- a/tests/ui/mut_range_bound.rs +++ b/tests/ui/mut_range_bound.rs @@ -76,7 +76,7 @@ fn mut_range_bound_no_immediate_break() { let mut n = 3; for i in n..10 { if n == 4 { - n = 1; // FIXME: warning because is is not immediately followed by break + n = 1; // FIXME: warning because it is not immediately followed by break let _ = 2; break; } diff --git a/tests/ui/mut_range_bound.stderr b/tests/ui/mut_range_bound.stderr index e0c8dced382e..b679b7a0aaf8 100644 --- a/tests/ui/mut_range_bound.stderr +++ b/tests/ui/mut_range_bound.stderr @@ -50,7 +50,7 @@ LL | m = 2; // warning because it is not immediately followed by break error: attempt to mutate range bound within loop --> $DIR/mut_range_bound.rs:79:13 | -LL | n = 1; // FIXME: warning because is is not immediately followed by break +LL | n = 1; // FIXME: warning because it is not immediately followed by break | ^ | = note: the range of the loop is unchanged diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 340e89d2db1d..85b6b639d554 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -385,3 +385,128 @@ mod used_more_than_once { fn use_x(_: impl AsRef) {} fn use_x_again(_: impl AsRef) {} } + +// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 +#[allow(dead_code)] +mod issue_9111 { + struct A; + + impl Extend for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + impl<'a> Extend<&'a u8> for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + fn main() { + let mut a = A; + a.extend(&[]); // vs a.extend([]); + } +} + +#[allow(dead_code)] +mod issue_9710 { + fn main() { + let string = String::new(); + for _i in 0..10 { + f(&string); + } + } + + fn f>(_: T) {} +} + +#[allow(dead_code)] +mod issue_9739 { + fn foo(_it: impl IntoIterator) {} + + fn main() { + foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } +} + +#[allow(dead_code)] +mod issue_9739_method_variant { + struct S; + + impl S { + fn foo(&self, _it: impl IntoIterator) {} + } + + fn main() { + S.foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } +} + +#[allow(dead_code)] +mod issue_9782 { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + // 100 + foo::<[u8; 100]>(a); + foo(a); + + // 16 + foo::<&[u8]>(&a); + foo(a.as_slice()); + + // 8 + foo::<&[u8; 100]>(&a); + foo(a); + } +} + +#[allow(dead_code)] +mod issue_9782_type_relative_variant { + struct S; + + impl S { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + S::foo::<&[u8; 100]>(&a); + } +} + +#[allow(dead_code)] +mod issue_9782_method_variant { + struct S; + + impl S { + fn foo>(&self, t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + S.foo::<&[u8; 100]>(&a); + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index c93711ac8e28..7b97bcf3817e 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -385,3 +385,128 @@ mod used_more_than_once { fn use_x(_: impl AsRef) {} fn use_x_again(_: impl AsRef) {} } + +// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 +#[allow(dead_code)] +mod issue_9111 { + struct A; + + impl Extend for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + impl<'a> Extend<&'a u8> for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + fn main() { + let mut a = A; + a.extend(&[]); // vs a.extend([]); + } +} + +#[allow(dead_code)] +mod issue_9710 { + fn main() { + let string = String::new(); + for _i in 0..10 { + f(&string); + } + } + + fn f>(_: T) {} +} + +#[allow(dead_code)] +mod issue_9739 { + fn foo(_it: impl IntoIterator) {} + + fn main() { + foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } +} + +#[allow(dead_code)] +mod issue_9739_method_variant { + struct S; + + impl S { + fn foo(&self, _it: impl IntoIterator) {} + } + + fn main() { + S.foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } +} + +#[allow(dead_code)] +mod issue_9782 { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + // 100 + foo::<[u8; 100]>(a); + foo(a); + + // 16 + foo::<&[u8]>(&a); + foo(a.as_slice()); + + // 8 + foo::<&[u8; 100]>(&a); + foo(&a); + } +} + +#[allow(dead_code)] +mod issue_9782_type_relative_variant { + struct S; + + impl S { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + S::foo::<&[u8; 100]>(&a); + } +} + +#[allow(dead_code)] +mod issue_9782_method_variant { + struct S; + + impl S { + fn foo>(&self, t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + S.foo::<&[u8; 100]>(&a); + } +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 8b593268bec2..485e6b84c868 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -210,5 +210,11 @@ error: the borrowed expression implements the required traits LL | use_x(&x); | ^^ help: change this to: `x` -error: aborting due to 35 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:474:13 + | +LL | foo(&a); + | ^^ help: change this to: `a` + +error: aborting due to 36 previous errors diff --git a/tests/ui/needless_borrowed_ref.fixed b/tests/ui/needless_borrowed_ref.fixed index bcb4eb2dd48a..0c47ceb7b679 100644 --- a/tests/ui/needless_borrowed_ref.fixed +++ b/tests/ui/needless_borrowed_ref.fixed @@ -1,11 +1,32 @@ // run-rustfix #![warn(clippy::needless_borrowed_reference)] -#![allow(unused, clippy::needless_borrow)] +#![allow( + unused, + irrefutable_let_patterns, + non_shorthand_field_patterns, + clippy::needless_borrow +)] fn main() {} -fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +struct Struct { + a: usize, + b: usize, + c: usize, +} + +struct TupleStruct(u8, u8, u8); + +fn should_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|a| a.is_empty()); @@ -24,16 +45,54 @@ fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec if let [a, b, ..] = slice {} if let [a, .., b] = slice {} if let [.., a, b] = slice {} + + if let [a, _] = slice {} + + if let (a, b, c) = &tuple {} + if let (a, _, c) = &tuple {} + if let (a, ..) = &tuple {} + + if let TupleStruct(a, ..) = &tuple_struct {} + + if let Struct { + a, + b: b, + c: renamed, + } = &s + {} + + if let Struct { a, b: _, .. } = &s {} } -fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +fn should_not_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { if let [ref a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} + if let &(ref a, b, ..) = &tuple {} + if let &TupleStruct(ref a, b, ..) = &tuple_struct {} + if let &Struct { ref a, b, .. } = &s {} + // must not be removed as variables must be bound consistently across | patterns if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} + // the `&`s here technically could be removed, but it'd be noisy and without a `ref` doesn't match + // the lint name + if let &[] = slice {} + if let &[_] = slice {} + if let &[..] = slice {} + if let &(..) = &tuple {} + if let &TupleStruct(..) = &tuple_struct {} + if let &Struct { .. } = &s {} + let mut var2 = 5; let thingy2 = Some(&mut var2); if let Some(&mut ref mut v) = thingy2 { @@ -59,6 +118,6 @@ fn foo(a: &Animal, b: &Animal) { // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted - (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + (Animal::Dog(a), &Animal::Dog(_)) => (), } } diff --git a/tests/ui/needless_borrowed_ref.rs b/tests/ui/needless_borrowed_ref.rs index f6de1a6d83d1..f883bb0c8891 100644 --- a/tests/ui/needless_borrowed_ref.rs +++ b/tests/ui/needless_borrowed_ref.rs @@ -1,11 +1,32 @@ // run-rustfix #![warn(clippy::needless_borrowed_reference)] -#![allow(unused, clippy::needless_borrow)] +#![allow( + unused, + irrefutable_let_patterns, + non_shorthand_field_patterns, + clippy::needless_borrow +)] fn main() {} -fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +struct Struct { + a: usize, + b: usize, + c: usize, +} + +struct TupleStruct(u8, u8, u8); + +fn should_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|&ref a| a.is_empty()); @@ -24,16 +45,54 @@ fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec if let &[ref a, ref b, ..] = slice {} if let &[ref a, .., ref b] = slice {} if let &[.., ref a, ref b] = slice {} + + if let &[ref a, _] = slice {} + + if let &(ref a, ref b, ref c) = &tuple {} + if let &(ref a, _, ref c) = &tuple {} + if let &(ref a, ..) = &tuple {} + + if let &TupleStruct(ref a, ..) = &tuple_struct {} + + if let &Struct { + ref a, + b: ref b, + c: ref renamed, + } = &s + {} + + if let &Struct { ref a, b: _, .. } = &s {} } -fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +fn should_not_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { if let [ref a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} + if let &(ref a, b, ..) = &tuple {} + if let &TupleStruct(ref a, b, ..) = &tuple_struct {} + if let &Struct { ref a, b, .. } = &s {} + // must not be removed as variables must be bound consistently across | patterns if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} + // the `&`s here technically could be removed, but it'd be noisy and without a `ref` doesn't match + // the lint name + if let &[] = slice {} + if let &[_] = slice {} + if let &[..] = slice {} + if let &(..) = &tuple {} + if let &TupleStruct(..) = &tuple_struct {} + if let &Struct { .. } = &s {} + let mut var2 = 5; let thingy2 = Some(&mut var2); if let Some(&mut ref mut v) = thingy2 { @@ -59,6 +118,6 @@ fn foo(a: &Animal, b: &Animal) { // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted - (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + (Animal::Dog(a), &Animal::Dog(_)) => (), } } diff --git a/tests/ui/needless_borrowed_ref.stderr b/tests/ui/needless_borrowed_ref.stderr index 7453542e673f..8d0f0c258dd2 100644 --- a/tests/ui/needless_borrowed_ref.stderr +++ b/tests/ui/needless_borrowed_ref.stderr @@ -1,5 +1,5 @@ error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:10:34 + --> $DIR/needless_borrowed_ref.rs:31:34 | LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); | ^^^^^^ @@ -12,7 +12,7 @@ LL + let _ = v.iter_mut().filter(|a| a.is_empty()); | error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:14:17 + --> $DIR/needless_borrowed_ref.rs:35:17 | LL | if let Some(&ref v) = thingy {} | ^^^^^^ @@ -24,7 +24,7 @@ LL + if let Some(v) = thingy {} | error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:16:14 + --> $DIR/needless_borrowed_ref.rs:37:14 | LL | if let &[&ref a, ref b] = slice_of_refs {} | ^^^^^^ @@ -36,7 +36,7 @@ LL + if let &[a, ref b] = slice_of_refs {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:18:9 + --> $DIR/needless_borrowed_ref.rs:39:9 | LL | let &[ref a, ..] = &array; | ^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + let [a, ..] = &array; | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:19:9 + --> $DIR/needless_borrowed_ref.rs:40:9 | LL | let &[ref a, ref b, ..] = &array; | ^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL + let [a, b, ..] = &array; | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:21:12 + --> $DIR/needless_borrowed_ref.rs:42:12 | LL | if let &[ref a, ref b] = slice {} | ^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL + if let [a, b] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:22:12 + --> $DIR/needless_borrowed_ref.rs:43:12 | LL | if let &[ref a, ref b] = &vec[..] {} | ^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL + if let [a, b] = &vec[..] {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:24:12 + --> $DIR/needless_borrowed_ref.rs:45:12 | LL | if let &[ref a, ref b, ..] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL + if let [a, b, ..] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:25:12 + --> $DIR/needless_borrowed_ref.rs:46:12 | LL | if let &[ref a, .., ref b] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL + if let [a, .., b] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:26:12 + --> $DIR/needless_borrowed_ref.rs:47:12 | LL | if let &[.., ref a, ref b] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -119,5 +119,96 @@ LL - if let &[.., ref a, ref b] = slice {} LL + if let [.., a, b] = slice {} | -error: aborting due to 10 previous errors +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:49:12 + | +LL | if let &[ref a, _] = slice {} + | ^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, _] = slice {} +LL + if let [a, _] = slice {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:51:12 + | +LL | if let &(ref a, ref b, ref c) = &tuple {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, ref b, ref c) = &tuple {} +LL + if let (a, b, c) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:52:12 + | +LL | if let &(ref a, _, ref c) = &tuple {} + | ^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, _, ref c) = &tuple {} +LL + if let (a, _, c) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:53:12 + | +LL | if let &(ref a, ..) = &tuple {} + | ^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, ..) = &tuple {} +LL + if let (a, ..) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:55:12 + | +LL | if let &TupleStruct(ref a, ..) = &tuple_struct {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &TupleStruct(ref a, ..) = &tuple_struct {} +LL + if let TupleStruct(a, ..) = &tuple_struct {} + | + +error: dereferencing a struct pattern where every field's pattern takes a reference + --> $DIR/needless_borrowed_ref.rs:57:12 + | +LL | if let &Struct { + | ____________^ +LL | | ref a, +LL | | b: ref b, +LL | | c: ref renamed, +LL | | } = &s + | |_____^ + | +help: try removing the `&` and `ref` parts + | +LL ~ if let Struct { +LL ~ a, +LL ~ b: b, +LL ~ c: renamed, + | + +error: dereferencing a struct pattern where every field's pattern takes a reference + --> $DIR/needless_borrowed_ref.rs:64:12 + | +LL | if let &Struct { ref a, b: _, .. } = &s {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &Struct { ref a, b: _, .. } = &s {} +LL + if let Struct { a, b: _, .. } = &s {} + | + +error: aborting due to 17 previous errors diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index 6ecbbcb62495..2659ad384885 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -33,4 +33,33 @@ fn main() { // `BinaryHeap` doesn't have `contains` method sample.iter().count(); sample.iter().next().is_none(); + + // Don't lint string from str + let _ = ["", ""].into_iter().collect::().is_empty(); + + let _ = sample.iter().next().is_none(); + let _ = sample.iter().any(|x| x == &0); + + struct VecWrapper(Vec); + impl core::ops::Deref for VecWrapper { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl IntoIterator for VecWrapper { + type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + impl FromIterator for VecWrapper { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } + } + + let _ = sample.iter().next().is_none(); + let _ = sample.iter().any(|x| x == &0); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 8dc69bcf5b38..535ec82982b1 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -33,4 +33,33 @@ fn main() { // `BinaryHeap` doesn't have `contains` method sample.iter().collect::>().len(); sample.iter().collect::>().is_empty(); + + // Don't lint string from str + let _ = ["", ""].into_iter().collect::().is_empty(); + + let _ = sample.iter().collect::>().is_empty(); + let _ = sample.iter().collect::>().contains(&&0); + + struct VecWrapper(Vec); + impl core::ops::Deref for VecWrapper { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl IntoIterator for VecWrapper { + type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + impl FromIterator for VecWrapper { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } + } + + let _ = sample.iter().collect::>().is_empty(); + let _ = sample.iter().collect::>().contains(&&0); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 039091627a8d..584d2a1d8356 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -66,5 +66,29 @@ error: avoid using `collect()` when not needed LL | sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` -error: aborting due to 11 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:40:27 + | +LL | let _ = sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:41:27 + | +LL | let _ = sample.iter().collect::>().contains(&&0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:63:27 + | +LL | let _ = sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:64:27 + | +LL | let _ = sample.iter().collect::>().contains(&&0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` + +error: aborting due to 15 previous errors diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 6d213b46c20c..fe4209e99b2f 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,5 @@ #![allow(clippy::uninlined_format_args)] +#![warn(clippy::needless_collect)] use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 99e1b91d8fea..790d725907f3 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:7:39 + --> $DIR/needless_collect_indirect.rs:8:39 | LL | let indirect_iter = sample.iter().collect::>(); | ^^^^^^^ @@ -14,7 +14,7 @@ LL ~ sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:38 + --> $DIR/needless_collect_indirect.rs:10:38 | LL | let indirect_len = sample.iter().collect::>(); | ^^^^^^^ @@ -28,7 +28,7 @@ LL ~ sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:11:40 + --> $DIR/needless_collect_indirect.rs:12:40 | LL | let indirect_empty = sample.iter().collect::>(); | ^^^^^^^ @@ -42,7 +42,7 @@ LL ~ sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:13:43 + --> $DIR/needless_collect_indirect.rs:14:43 | LL | let indirect_contains = sample.iter().collect::>(); | ^^^^^^^ @@ -56,7 +56,7 @@ LL ~ sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:25:48 + --> $DIR/needless_collect_indirect.rs:26:48 | LL | let non_copy_contains = sample.into_iter().collect::>(); | ^^^^^^^ @@ -70,7 +70,7 @@ LL ~ sample.into_iter().any(|x| x == a); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:54:51 + --> $DIR/needless_collect_indirect.rs:55:51 | LL | let buffer: Vec<&str> = string.split('/').collect(); | ^^^^^^^ @@ -84,7 +84,7 @@ LL ~ string.split('/').count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:59:55 + --> $DIR/needless_collect_indirect.rs:60:55 | LL | let indirect_len: VecDeque<_> = sample.iter().collect(); | ^^^^^^^ @@ -98,7 +98,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:64:57 + --> $DIR/needless_collect_indirect.rs:65:57 | LL | let indirect_len: LinkedList<_> = sample.iter().collect(); | ^^^^^^^ @@ -112,7 +112,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:69:57 + --> $DIR/needless_collect_indirect.rs:70:57 | LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); | ^^^^^^^ @@ -126,7 +126,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:129:59 + --> $DIR/needless_collect_indirect.rs:130:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -143,7 +143,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == i); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:154:59 + --> $DIR/needless_collect_indirect.rs:155:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -160,7 +160,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:183:63 + --> $DIR/needless_collect_indirect.rs:184:63 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -177,7 +177,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:219:59 + --> $DIR/needless_collect_indirect.rs:220:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -195,7 +195,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:244:26 + --> $DIR/needless_collect_indirect.rs:245:26 | LL | let w = v.iter().collect::>(); | ^^^^^^^ @@ -211,7 +211,7 @@ LL ~ for _ in 0..v.iter().count() { | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:266:30 + --> $DIR/needless_collect_indirect.rs:267:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ @@ -227,7 +227,7 @@ LL ~ while 1 == v.iter().count() { | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:288:30 + --> $DIR/needless_collect_indirect.rs:289:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index fc686b1dac0e..2efc936752ef 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -29,11 +29,20 @@ fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 { x } -// No error; multiple input refs. -fn multiple_in_and_out_2<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 +// ^^^ +fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { x } +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 +// ^^^ +fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { + y +} + // No error; multiple input refs async fn func<'a>(args: &[&'a str]) -> Option<&'a str> { args.get(0).cloned() @@ -44,11 +53,20 @@ fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 { x } -// No error. -fn deep_reference_1<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> +// ^^^ +fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { Ok(x) } +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> +// ^^^ +fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { + Ok(y) +} + // No error; two input refs. fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 { x.unwrap() @@ -129,11 +147,20 @@ impl X { &self.x } - // No error; multiple input refs. - fn self_and_in_out<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 + // ^^^ + fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { &self.x } + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 + // ^^^^^ + fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { + x + } + fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} // No error; same lifetimes on two params. @@ -167,8 +194,19 @@ fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str { unimplemented!() } -// No warning; two input lifetimes. -fn struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str +// ^^ +fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str +// ^^^^ +fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { unimplemented!() } @@ -203,8 +241,19 @@ fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str { unimplemented!() } -// No warning; two input lifetimes. -fn alias_with_lt4<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str +// ^^ +fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str +// ^^^^^^^^^ +fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { unimplemented!() } @@ -419,4 +468,31 @@ mod issue7296 { } } +mod pr_9743_false_negative_fix { + #![allow(unused)] + + fn foo<'a>(x: &'a u8, y: &'_ u8) {} + + fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} +} + +mod pr_9743_output_lifetime_checks { + #![allow(unused)] + + // lint: only one input + fn one_input<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + + // lint: multiple inputs, output would not be elided + fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { + unimplemented!() + } + + // don't lint: multiple inputs, output would be elided (which would create an ambiguity) + fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'a u8 { + unimplemented!() + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index 3c428fd4674c..5a7cf13c86dd 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,4 +1,4 @@ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a, 'b --> $DIR/needless_lifetimes.rs:11:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} @@ -6,185 +6,311 @@ LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a, 'b --> $DIR/needless_lifetimes.rs:13:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a --> $DIR/needless_lifetimes.rs:23:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:57:1 +error: the following explicit lifetimes could be elided: 'b + --> $DIR/needless_lifetimes.rs:35:1 + | +LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:42:1 + | +LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the following explicit lifetimes could be elided: 'b + --> $DIR/needless_lifetimes.rs:59:1 + | +LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:66:1 + | +LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:75:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:62:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:80:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:74:1 +error: the following explicit lifetimes could be elided: 'a, 'b + --> $DIR/needless_lifetimes.rs:92:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace with `'_` in generic arguments such as here + --> $DIR/needless_lifetimes.rs:92:37 + | +LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} + | ^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:98:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:116:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace with `'_` in generic arguments such as here + --> $DIR/needless_lifetimes.rs:116:32 + | +LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> + | ^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:128:5 +error: the following explicit lifetimes could be elided: 's + --> $DIR/needless_lifetimes.rs:146:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:137:5 +error: the following explicit lifetimes could be elided: 't + --> $DIR/needless_lifetimes.rs:153:5 + | +LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the following explicit lifetimes could be elided: 's + --> $DIR/needless_lifetimes.rs:160:5 + | +LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the following explicit lifetimes could be elided: 's, 't + --> $DIR/needless_lifetimes.rs:164:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:156:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:183:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace with `'_` in generic arguments such as here + --> $DIR/needless_lifetimes.rs:183:33 + | +LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { + | ^^ + +error: the following explicit lifetimes could be elided: 'b + --> $DIR/needless_lifetimes.rs:201:1 + | +LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace with `'_` in generic arguments such as here + --> $DIR/needless_lifetimes.rs:201:43 + | +LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { + | ^^ + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:209:1 + | +LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:186:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:224:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:192:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:230:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace with `'_` in generic arguments such as here + --> $DIR/needless_lifetimes.rs:230:37 + | +LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { + | ^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:211:1 +error: the following explicit lifetimes could be elided: 'b + --> $DIR/needless_lifetimes.rs:248:1 + | +LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace with `'_` in generic arguments such as here + --> $DIR/needless_lifetimes.rs:248:47 + | +LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { + | ^^ + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:256:1 + | +LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:260:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:219:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:268:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:255:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:304:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace with `'_` in generic arguments such as here + --> $DIR/needless_lifetimes.rs:304:47 + | +LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { + | ^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:262:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:311:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:266:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:315:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:279:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:328:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:311:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:360:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:320:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:369:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:332:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:381:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:347:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:396:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:360:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:409:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:363:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:412:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:385:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:434:9 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:388:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:437:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:399:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:448:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:405:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:454:9 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:406:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:455:9 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:415:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:464:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:416:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:465:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 31 previous errors +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:474:5 + | +LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:476:5 + | +LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:483:5 + | +LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:488:5 + | +LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 45 previous errors diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 3dbef19890e9..28e8f459d442 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -229,6 +229,27 @@ pub fn test18() { }; } +// Issue #9831: unconditional break to internal labeled block +pub fn test19() { + fn thing(iter: impl Iterator) { + for _ in iter { + 'b: { + break 'b; + } + } + } +} + +pub fn test20() { + 'a: loop { + 'b: { + break 'b 'c: { + break 'a; + }; + } + } +} + fn main() { test1(); test2(); diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 3033f019244a..b7029bf8bed4 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -114,5 +114,17 @@ LL | | break x; LL | | }; | |_____^ -error: aborting due to 10 previous errors +error: this loop never actually loops + --> $DIR/never_loop.rs:244:5 + | +LL | / 'a: loop { +LL | | 'b: { +LL | | break 'b 'c: { +LL | | break 'a; +LL | | }; +LL | | } +LL | | } + | |_____^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index 2f315ffe2983..f69982d63a89 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -350,3 +350,53 @@ impl RetOtherSelf { RetOtherSelf(RetOtherSelfWrapper(t)) } } + +mod issue7344 { + struct RetImplTraitSelf(T); + + impl RetImplTraitSelf { + // should not trigger lint + fn new(t: T) -> impl Into { + Self(t) + } + } + + struct RetImplTraitNoSelf(T); + + impl RetImplTraitNoSelf { + // should trigger lint + fn new(t: T) -> impl Into { + 1 + } + } + + trait Trait2 {} + impl Trait2 for () {} + + struct RetImplTraitSelf2(T); + + impl RetImplTraitSelf2 { + // should not trigger lint + fn new(t: T) -> impl Trait2<(), Self> { + unimplemented!() + } + } + + struct RetImplTraitNoSelf2(T); + + impl RetImplTraitNoSelf2 { + // should trigger lint + fn new(t: T) -> impl Trait2<(), i32> { + unimplemented!() + } + } + + struct RetImplTraitSelfAdt<'a>(&'a str); + + impl<'a> RetImplTraitSelfAdt<'a> { + // should not trigger lint + fn new<'b: 'a>(s: &'b str) -> impl Into> { + RetImplTraitSelfAdt(s) + } + } +} diff --git a/tests/ui/new_ret_no_self.stderr b/tests/ui/new_ret_no_self.stderr index 8217bc6187f9..bc13be47927b 100644 --- a/tests/ui/new_ret_no_self.stderr +++ b/tests/ui/new_ret_no_self.stderr @@ -76,5 +76,21 @@ LL | | unimplemented!(); LL | | } | |_________^ -error: aborting due to 10 previous errors +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:368:9 + | +LL | / fn new(t: T) -> impl Into { +LL | | 1 +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:389:9 + | +LL | / fn new(t: T) -> impl Trait2<(), i32> { +LL | | unimplemented!() +LL | | } + | |_________^ + +error: aborting due to 12 previous errors diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index f15ac551bb3c..0456005dce49 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -189,3 +189,12 @@ fn main() { let _ = res.map_or(1, |a| a + 1); let _ = res.map_or(5, |a| a + 1); } + +#[allow(dead_code)] +fn issue9742() -> Option<&'static str> { + // should not lint because of guards + match Some("foo ") { + Some(name) if name.starts_with("foo") => Some(name.trim()), + _ => None, + } +} diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 9eeaea12d3bc..23b148752cbf 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -230,3 +230,12 @@ fn main() { }; let _ = if let Ok(a) = res { a + 1 } else { 5 }; } + +#[allow(dead_code)] +fn issue9742() -> Option<&'static str> { + // should not lint because of guards + match Some("foo ") { + Some(name) if name.starts_with("foo") => Some(name.trim()), + _ => None, + } +} diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 23b1aa8bebd5..be9a65506e13 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -236,4 +236,20 @@ mod issue9608 { } } +mod issue8993 { + fn g() -> i32 { + 3 + } + + fn f(n: i32) -> i32 { + n + } + + fn test_map_or() { + let _ = Some(4).map_or_else(g, |v| v); + let _ = Some(4).map_or_else(g, f); + let _ = Some(4).map_or(0, f); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 039998f22dd7..628c97046389 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -236,4 +236,20 @@ mod issue9608 { } } +mod issue8993 { + fn g() -> i32 { + 3 + } + + fn f(n: i32) -> i32 { + n + } + + fn test_map_or() { + let _ = Some(4).map_or(g(), |v| v); + let _ = Some(4).map_or(g(), f); + let _ = Some(4).map_or(0, f); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 113ba150c619..ba3001db7a5f 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -156,5 +156,17 @@ error: use of `unwrap_or` followed by a call to `new` LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` -error: aborting due to 26 previous errors +error: use of `map_or` followed by a function call + --> $DIR/or_fun_call.rs:249:25 + | +LL | let _ = Some(4).map_or(g(), |v| v); + | ^^^^^^^^^^^^^^^^^^ help: try this: `map_or_else(g, |v| v)` + +error: use of `map_or` followed by a function call + --> $DIR/or_fun_call.rs:250:25 + | +LL | let _ = Some(4).map_or(g(), f); + | ^^^^^^^^^^^^^^ help: try this: `map_or_else(g, f)` + +error: aborting due to 28 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 993389232cc2..5c49d46da726 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -134,6 +134,9 @@ fn result_func(x: Result) -> Result { return func_returning_result(); } + // no warning + let _ = if let Err(e) = x { Err(e) } else { Ok(0) }; + Ok(y) } diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 9ae0d88829af..d057df6a9b35 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -166,6 +166,9 @@ fn result_func(x: Result) -> Result { return func_returning_result(); } + // no warning + let _ = if let Err(e) = x { Err(e) } else { Ok(0) }; + Ok(y) } diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 1b6cd524b2f2..23172d7e535d 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -115,7 +115,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:193:5 + --> $DIR/question_mark.rs:196:5 | LL | / if let Err(err) = func_returning_result() { LL | | return Err(err); @@ -123,7 +123,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:200:5 + --> $DIR/question_mark.rs:203:5 | LL | / if let Err(err) = func_returning_result() { LL | | return Err(err); diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 8beae8dee085..689928f04794 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -12,7 +12,6 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] #![allow(clippy::overly_complex_bool_expr)] @@ -27,9 +26,11 @@ #![allow(clippy::recursive_format_impl)] #![allow(clippy::invisible_characters)] #![allow(drop_bounds)] +#![allow(for_loops_over_fallibles)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] +#![allow(let_underscore_drop)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] @@ -45,8 +46,6 @@ #![warn(clippy::disallowed_methods)] #![warn(clippy::disallowed_types)] #![warn(clippy::mixed_read_write_in_expression)] -#![warn(for_loops_over_fallibles)] -#![warn(for_loops_over_fallibles)] #![warn(clippy::useless_conversion)] #![warn(clippy::match_result_ok)] #![warn(clippy::overly_complex_bool_expr)] @@ -66,9 +65,12 @@ #![warn(clippy::invisible_characters)] #![warn(drop_bounds)] #![warn(for_loops_over_fallibles)] +#![warn(for_loops_over_fallibles)] +#![warn(for_loops_over_fallibles)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] +#![warn(let_underscore_drop)] #![warn(enum_intrinsics_non_enums)] #![warn(non_fmt_panics)] #![warn(named_arguments_used_positionally)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 9e665047baae..b74aa650ffd4 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -12,7 +12,6 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] #![allow(clippy::overly_complex_bool_expr)] @@ -27,9 +26,11 @@ #![allow(clippy::recursive_format_impl)] #![allow(clippy::invisible_characters)] #![allow(drop_bounds)] +#![allow(for_loops_over_fallibles)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] +#![allow(let_underscore_drop)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] @@ -45,8 +46,6 @@ #![warn(clippy::disallowed_method)] #![warn(clippy::disallowed_type)] #![warn(clippy::eval_order_dependence)] -#![warn(clippy::for_loop_over_option)] -#![warn(clippy::for_loop_over_result)] #![warn(clippy::identity_conversion)] #![warn(clippy::if_let_some_result)] #![warn(clippy::logic_bug)] @@ -65,10 +64,13 @@ #![warn(clippy::to_string_in_display)] #![warn(clippy::zero_width_space)] #![warn(clippy::drop_bounds)] +#![warn(clippy::for_loop_over_option)] +#![warn(clippy::for_loop_over_result)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] #![warn(clippy::invalid_ref)] +#![warn(clippy::let_underscore_drop)] #![warn(clippy::mem_discriminant_non_enum)] #![warn(clippy::panic_params)] #![warn(clippy::positional_named_format_parameters)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 63eb565185f0..622a32c5908a 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:39:9 + --> $DIR/rename.rs:40:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` @@ -7,232 +7,238 @@ LL | #![warn(clippy::blacklisted_name)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:40:9 + --> $DIR/rename.rs:41:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:41:9 + --> $DIR/rename.rs:42:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:42:9 + --> $DIR/rename.rs:43:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:43:9 + --> $DIR/rename.rs:44:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:44:9 + --> $DIR/rename.rs:45:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:45:9 + --> $DIR/rename.rs:46:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:46:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:47:9 + --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` -error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:48:9 - | -LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` - -error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:49:9 - | -LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` - error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` +error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` + --> $DIR/rename.rs:67:9 + | +LL | #![warn(clippy::for_loop_over_option)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` + +error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` --> $DIR/rename.rs:68:9 | +LL | #![warn(clippy::for_loop_over_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` + +error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` + --> $DIR/rename.rs:69:9 + | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` +error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` + --> $DIR/rename.rs:73:9 + | +LL | #![warn(clippy::let_underscore_drop)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` + error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 39 previous errors +error: aborting due to 40 previous errors diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index f7df3b856550..9dd27d6dc01a 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -50,6 +50,18 @@ impl LargeErrorVariants<()> { } } +enum MultipleLargeVariants { + _Biggest([u8; 1024]), + _AlsoBig([u8; 512]), + _Ok(usize), +} + +impl MultipleLargeVariants { + fn large_enum_error() -> Result<(), Self> { + Ok(()) + } +} + trait TraitForcesLargeError { fn large_error() -> Result<(), [u8; 512]> { Ok(()) diff --git a/tests/ui/result_large_err.stderr b/tests/ui/result_large_err.stderr index bea101fe20bf..c386edfd2157 100644 --- a/tests/ui/result_large_err.stderr +++ b/tests/ui/result_large_err.stderr @@ -42,13 +42,29 @@ LL | pub fn param_large_error() -> Result<(), (u128, R, FullyDefinedLargeErro error: the `Err`-variant returned from this function is very large --> $DIR/result_large_err.rs:48:34 | +LL | _Omg([u8; 512]), + | --------------- the largest variant contains at least 512 bytes +... LL | pub fn large_enum_error() -> Result<(), Self> { - | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes + | ^^^^^^^^^^^^^^^^ | = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:54:25 + --> $DIR/result_large_err.rs:60:30 + | +LL | _Biggest([u8; 1024]), + | -------------------- the largest variant contains at least 1024 bytes +LL | _AlsoBig([u8; 512]), + | ------------------- the variant `_AlsoBig` contains at least 512 bytes +... +LL | fn large_enum_error() -> Result<(), Self> { + | ^^^^^^^^^^^^^^^^ + | + = help: try reducing the size of `MultipleLargeVariants`, for example by boxing large elements or replacing it with `Box` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:66:25 | LL | fn large_error() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -56,7 +72,7 @@ LL | fn large_error() -> Result<(), [u8; 512]> { = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:73:29 + --> $DIR/result_large_err.rs:85:29 | LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -64,7 +80,7 @@ LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:82:40 + --> $DIR/result_large_err.rs:94:40 | LL | pub fn param_large_union() -> Result<(), UnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -72,7 +88,7 @@ LL | pub fn param_large_union() -> Result<(), UnionError> { = help: try reducing the size of `UnionError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:91:34 + --> $DIR/result_large_err.rs:103:34 | LL | pub fn array_error_subst() -> Result<(), ArrayError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes @@ -80,12 +96,12 @@ LL | pub fn array_error_subst() -> Result<(), ArrayError> { = help: try reducing the size of `ArrayError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:95:31 + --> $DIR/result_large_err.rs:107:31 | LL | pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes | = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box>` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/seek_from_current.fixed b/tests/ui/seek_from_current.fixed new file mode 100644 index 000000000000..4b5303324bc6 --- /dev/null +++ b/tests/ui/seek_from_current.fixed @@ -0,0 +1,26 @@ +// run-rustfix +#![warn(clippy::seek_from_current)] +#![feature(custom_inner_attributes)] + +use std::fs::File; +use std::io::{self, Seek, SeekFrom, Write}; + +fn _msrv_1_50() -> io::Result<()> { + #![clippy::msrv = "1.50"] + let mut f = File::create("foo.txt")?; + f.write_all(b"Hi!")?; + f.seek(SeekFrom::Current(0))?; + f.seek(SeekFrom::Current(1))?; + Ok(()) +} + +fn _msrv_1_51() -> io::Result<()> { + #![clippy::msrv = "1.51"] + let mut f = File::create("foo.txt")?; + f.write_all(b"Hi!")?; + f.stream_position()?; + f.seek(SeekFrom::Current(1))?; + Ok(()) +} + +fn main() {} diff --git a/tests/ui/seek_from_current.rs b/tests/ui/seek_from_current.rs new file mode 100644 index 000000000000..f93639261a18 --- /dev/null +++ b/tests/ui/seek_from_current.rs @@ -0,0 +1,26 @@ +// run-rustfix +#![warn(clippy::seek_from_current)] +#![feature(custom_inner_attributes)] + +use std::fs::File; +use std::io::{self, Seek, SeekFrom, Write}; + +fn _msrv_1_50() -> io::Result<()> { + #![clippy::msrv = "1.50"] + let mut f = File::create("foo.txt")?; + f.write_all(b"Hi!")?; + f.seek(SeekFrom::Current(0))?; + f.seek(SeekFrom::Current(1))?; + Ok(()) +} + +fn _msrv_1_51() -> io::Result<()> { + #![clippy::msrv = "1.51"] + let mut f = File::create("foo.txt")?; + f.write_all(b"Hi!")?; + f.seek(SeekFrom::Current(0))?; + f.seek(SeekFrom::Current(1))?; + Ok(()) +} + +fn main() {} diff --git a/tests/ui/seek_from_current.stderr b/tests/ui/seek_from_current.stderr new file mode 100644 index 000000000000..db1125b53cdf --- /dev/null +++ b/tests/ui/seek_from_current.stderr @@ -0,0 +1,10 @@ +error: using `SeekFrom::Current` to start from current position + --> $DIR/seek_from_current.rs:21:5 + | +LL | f.seek(SeekFrom::Current(0))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `f.stream_position()` + | + = note: `-D clippy::seek-from-current` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/seek_to_start_instead_of_rewind.fixed b/tests/ui/seek_to_start_instead_of_rewind.fixed new file mode 100644 index 000000000000..464b6cdef639 --- /dev/null +++ b/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -0,0 +1,137 @@ +// run-rustfix +#![allow(unused)] +#![feature(custom_inner_attributes)] +#![warn(clippy::seek_to_start_instead_of_rewind)] + +use std::fs::OpenOptions; +use std::io::{Read, Seek, SeekFrom, Write}; + +struct StructWithSeekMethod {} + +impl StructWithSeekMethod { + fn seek(&mut self, from: SeekFrom) {} +} + +trait MySeekTrait { + fn seek(&mut self, from: SeekFrom) {} +} + +struct StructWithSeekTrait {} +impl MySeekTrait for StructWithSeekTrait {} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait_bound(t: &mut T) { + t.seek(SeekFrom::Start(0)); +} + +// This should trigger clippy warning +fn seek_to_start(t: &mut T) { + t.rewind(); +} + +// This should trigger clippy warning +fn owned_seek_to_start(mut t: T) { + t.rewind(); +} + +// This should NOT trigger clippy warning because +// it does not seek to start +fn seek_to_5(t: &mut T) { + t.seek(SeekFrom::Start(5)); +} + +// This should NOT trigger clippy warning because +// it does not seek to start +fn seek_to_end(t: &mut T) { + t.seek(SeekFrom::End(0)); +} + +fn main() { + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let mut my_struct_trait = StructWithSeekTrait {}; + seek_to_start_false_trait_bound(&mut my_struct_trait); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + seek_to_5(&mut f); + seek_to_end(&mut f); + seek_to_start(&mut f); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} + +fn msrv_1_54() { + #![clippy::msrv = "1.54"] + + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + + f.seek(SeekFrom::Start(0)); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} + +fn msrv_1_55() { + #![clippy::msrv = "1.55"] + + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + + f.rewind(); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} diff --git a/tests/ui/seek_to_start_instead_of_rewind.rs b/tests/ui/seek_to_start_instead_of_rewind.rs new file mode 100644 index 000000000000..68e09bd7c1f0 --- /dev/null +++ b/tests/ui/seek_to_start_instead_of_rewind.rs @@ -0,0 +1,137 @@ +// run-rustfix +#![allow(unused)] +#![feature(custom_inner_attributes)] +#![warn(clippy::seek_to_start_instead_of_rewind)] + +use std::fs::OpenOptions; +use std::io::{Read, Seek, SeekFrom, Write}; + +struct StructWithSeekMethod {} + +impl StructWithSeekMethod { + fn seek(&mut self, from: SeekFrom) {} +} + +trait MySeekTrait { + fn seek(&mut self, from: SeekFrom) {} +} + +struct StructWithSeekTrait {} +impl MySeekTrait for StructWithSeekTrait {} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait_bound(t: &mut T) { + t.seek(SeekFrom::Start(0)); +} + +// This should trigger clippy warning +fn seek_to_start(t: &mut T) { + t.seek(SeekFrom::Start(0)); +} + +// This should trigger clippy warning +fn owned_seek_to_start(mut t: T) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// it does not seek to start +fn seek_to_5(t: &mut T) { + t.seek(SeekFrom::Start(5)); +} + +// This should NOT trigger clippy warning because +// it does not seek to start +fn seek_to_end(t: &mut T) { + t.seek(SeekFrom::End(0)); +} + +fn main() { + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let mut my_struct_trait = StructWithSeekTrait {}; + seek_to_start_false_trait_bound(&mut my_struct_trait); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + seek_to_5(&mut f); + seek_to_end(&mut f); + seek_to_start(&mut f); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} + +fn msrv_1_54() { + #![clippy::msrv = "1.54"] + + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + + f.seek(SeekFrom::Start(0)); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} + +fn msrv_1_55() { + #![clippy::msrv = "1.55"] + + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + + f.seek(SeekFrom::Start(0)); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} diff --git a/tests/ui/seek_to_start_instead_of_rewind.stderr b/tests/ui/seek_to_start_instead_of_rewind.stderr new file mode 100644 index 000000000000..de0eec5d909c --- /dev/null +++ b/tests/ui/seek_to_start_instead_of_rewind.stderr @@ -0,0 +1,22 @@ +error: used `seek` to go to the start of the stream + --> $DIR/seek_to_start_instead_of_rewind.rs:54:7 + | +LL | t.seek(SeekFrom::Start(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` + | + = note: `-D clippy::seek-to-start-instead-of-rewind` implied by `-D warnings` + +error: used `seek` to go to the start of the stream + --> $DIR/seek_to_start_instead_of_rewind.rs:59:7 + | +LL | t.seek(SeekFrom::Start(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` + +error: used `seek` to go to the start of the stream + --> $DIR/seek_to_start_instead_of_rewind.rs:131:7 + | +LL | f.seek(SeekFrom::Start(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/single_component_path_imports.stderr b/tests/ui/single_component_path_imports.stderr index 509c88ac256a..71dcc25d6e5b 100644 --- a/tests/ui/single_component_path_imports.stderr +++ b/tests/ui/single_component_path_imports.stderr @@ -1,16 +1,16 @@ error: this import is redundant - --> $DIR/single_component_path_imports.rs:23:5 + --> $DIR/single_component_path_imports.rs:5:1 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely | = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant - --> $DIR/single_component_path_imports.rs:5:1 + --> $DIR/single_component_path_imports.rs:23:5 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely error: aborting due to 2 previous errors diff --git a/tests/ui/single_component_path_imports_nested_first.stderr b/tests/ui/single_component_path_imports_nested_first.stderr index 633546f6419a..330f285202d0 100644 --- a/tests/ui/single_component_path_imports_nested_first.stderr +++ b/tests/ui/single_component_path_imports_nested_first.stderr @@ -1,3 +1,11 @@ +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:4:1 + | +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely + | + = note: `-D clippy::single-component-path-imports` implied by `-D warnings` + error: this import is redundant --> $DIR/single_component_path_imports_nested_first.rs:13:10 | @@ -5,7 +13,6 @@ LL | use {regex, serde}; | ^^^^^ | = help: remove this import - = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant --> $DIR/single_component_path_imports_nested_first.rs:13:17 @@ -15,11 +22,5 @@ LL | use {regex, serde}; | = help: remove this import -error: this import is redundant - --> $DIR/single_component_path_imports_nested_first.rs:4:1 - | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely - error: aborting due to 3 previous errors diff --git a/tests/ui/string_extend.fixed b/tests/ui/string_extend.fixed index 1883a9f83257..d200d7310fca 100644 --- a/tests/ui/string_extend.fixed +++ b/tests/ui/string_extend.fixed @@ -29,4 +29,7 @@ fn main() { let f = HasChars; s.extend(f.chars()); + + // issue #9735 + s.push_str(&abc[0..2]); } diff --git a/tests/ui/string_extend.rs b/tests/ui/string_extend.rs index 07d0baa1be6c..0dd96a3b2103 100644 --- a/tests/ui/string_extend.rs +++ b/tests/ui/string_extend.rs @@ -29,4 +29,7 @@ fn main() { let f = HasChars; s.extend(f.chars()); + + // issue #9735 + s.extend(abc[0..2].chars()); } diff --git a/tests/ui/string_extend.stderr b/tests/ui/string_extend.stderr index 6af8c9e1662b..b35c77fd9611 100644 --- a/tests/ui/string_extend.stderr +++ b/tests/ui/string_extend.stderr @@ -18,5 +18,11 @@ error: calling `.extend(_.chars())` LL | s.extend(def.chars()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&def)` -error: aborting due to 3 previous errors +error: calling `.extend(_.chars())` + --> $DIR/string_extend.rs:34:5 + | +LL | s.extend(abc[0..2].chars()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&abc[0..2])` + +error: aborting due to 4 previous errors diff --git a/tests/ui/suspicious_xor_used_as_pow.rs b/tests/ui/suspicious_xor_used_as_pow.rs new file mode 100644 index 000000000000..eb9fc63fb1d4 --- /dev/null +++ b/tests/ui/suspicious_xor_used_as_pow.rs @@ -0,0 +1,34 @@ +#![allow(unused)] +#![warn(clippy::suspicious_xor_used_as_pow)] +#![allow(clippy::eq_op)] + +macro_rules! macro_test { + () => { + 13 + }; +} + +macro_rules! macro_test_inside { + () => { + 1 ^ 2 // should warn even if inside macro + }; +} + +fn main() { + // Should warn: + let _ = 2 ^ 5; + let _ = 2i32 ^ 9i32; + let _ = 2i32 ^ 2i32; + let _ = 50i32 ^ 3i32; + let _ = 5i32 ^ 8i32; + let _ = 2i32 ^ 32i32; + macro_test_inside!(); + + // Should not warn: + let x = 0x02; + let _ = x ^ 2; + let _ = 2 ^ x; + let _ = x ^ 5; + let _ = 10 ^ 0b0101; + let _ = 2i32 ^ macro_test!(); +} diff --git a/tests/ui/suspicious_xor_used_as_pow.stderr b/tests/ui/suspicious_xor_used_as_pow.stderr new file mode 100644 index 000000000000..8bb3c8fbeebd --- /dev/null +++ b/tests/ui/suspicious_xor_used_as_pow.stderr @@ -0,0 +1,51 @@ +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:19:13 + | +LL | let _ = 2 ^ 5; + | ^^^^^ help: did you mean to write: `2.pow(5)` + | + = note: `-D clippy::suspicious-xor-used-as-pow` implied by `-D warnings` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:20:13 + | +LL | let _ = 2i32 ^ 9i32; + | ^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(9_i32)` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:21:13 + | +LL | let _ = 2i32 ^ 2i32; + | ^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(2_i32)` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:22:13 + | +LL | let _ = 50i32 ^ 3i32; + | ^^^^^^^^^^^^ help: did you mean to write: `50_i32.pow(3_i32)` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:23:13 + | +LL | let _ = 5i32 ^ 8i32; + | ^^^^^^^^^^^ help: did you mean to write: `5_i32.pow(8_i32)` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:24:13 + | +LL | let _ = 2i32 ^ 32i32; + | ^^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(32_i32)` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:13:9 + | +LL | 1 ^ 2 // should warn even if inside macro + | ^^^^^ help: did you mean to write: `1.pow(2)` +... +LL | macro_test_inside!(); + | -------------------- in this macro invocation + | + = note: this error originates in the macro `macro_test_inside` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 7 previous errors + diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index 24b229235d33..805a2ba5a598 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -155,3 +155,12 @@ fn issue_8154() { let s = S3(&mut s); std::mem::swap(&mut s.0.x, &mut s.0.y); } + +const fn issue_9864(mut u: u32) -> u32 { + let mut v = 10; + + let temp = u; + u = v; + v = temp; + u + v +} diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index a318c27919c8..a8c878479523 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -179,3 +179,12 @@ fn issue_8154() { s.0.x = s.0.y; s.0.y = t; } + +const fn issue_9864(mut u: u32) -> u32 { + let mut v = 10; + + let temp = u; + u = v; + v = temp; + u + v +} diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 001c910239af..1cbacf0feab5 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::borrow_as_ptr)] +#![allow(dead_code, clippy::borrow_as_ptr, clippy::needless_lifetimes)] extern crate core; diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index af4f3b18443b..c0af011d33d0 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -3,6 +3,7 @@ #![deny(clippy::trivially_copy_pass_by_ref)] #![allow( clippy::disallowed_names, + clippy::needless_lifetimes, clippy::redundant_field_names, clippy::uninlined_format_args )] diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index 6a8eca965534..8c5cfa8a0f17 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -1,5 +1,5 @@ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:50:11 + --> $DIR/trivially_copy_pass_by_ref.rs:51:11 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` @@ -11,103 +11,103 @@ LL | #![deny(clippy::trivially_copy_pass_by_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:50:20 + --> $DIR/trivially_copy_pass_by_ref.rs:51:20 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:50:29 + --> $DIR/trivially_copy_pass_by_ref.rs:51:29 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:57:12 + --> $DIR/trivially_copy_pass_by_ref.rs:58:12 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^^ help: consider passing by value instead: `self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:57:22 + --> $DIR/trivially_copy_pass_by_ref.rs:58:22 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:57:31 + --> $DIR/trivially_copy_pass_by_ref.rs:58:31 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:57:40 + --> $DIR/trivially_copy_pass_by_ref.rs:58:40 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:59:16 + --> $DIR/trivially_copy_pass_by_ref.rs:60:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:59:25 + --> $DIR/trivially_copy_pass_by_ref.rs:60:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:59:34 + --> $DIR/trivially_copy_pass_by_ref.rs:60:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:61:35 + --> $DIR/trivially_copy_pass_by_ref.rs:62:35 | LL | fn bad_issue7518(self, other: &Self) {} | ^^^^^ help: consider passing by value instead: `Self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:73:16 + --> $DIR/trivially_copy_pass_by_ref.rs:74:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:73:25 + --> $DIR/trivially_copy_pass_by_ref.rs:74:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:73:34 + --> $DIR/trivially_copy_pass_by_ref.rs:74:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:77:34 + --> $DIR/trivially_copy_pass_by_ref.rs:78:34 | LL | fn trait_method(&self, _foo: &Foo); | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:109:21 + --> $DIR/trivially_copy_pass_by_ref.rs:110:21 | LL | fn foo_never(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:114:15 + --> $DIR/trivially_copy_pass_by_ref.rs:115:15 | LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:141:37 + --> $DIR/trivially_copy_pass_by_ref.rs:142:37 | LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { | ^^^^^^^ help: consider passing by value instead: `u32` diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_duration_subtraction.fixed new file mode 100644 index 000000000000..a0e49a8beb1e --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.fixed @@ -0,0 +1,17 @@ +// run-rustfix +#![warn(clippy::unchecked_duration_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first.checked_sub(second).unwrap(); + + let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); + + let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); + + let _ = Instant::now().checked_sub(second).unwrap(); +} diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs new file mode 100644 index 000000000000..a14a7ea57cc5 --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.rs @@ -0,0 +1,17 @@ +// run-rustfix +#![warn(clippy::unchecked_duration_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first - second; + + let _ = Instant::now() - Duration::from_secs(5); + + let _ = _first - Duration::from_secs(5); + + let _ = Instant::now() - second; +} diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr new file mode 100644 index 000000000000..a2e0aa1d7c08 --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -0,0 +1,28 @@ +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:10:13 + | +LL | let _ = _first - second; + | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` + | + = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:12:13 + | +LL | let _ = Instant::now() - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:14:13 + | +LL | let _ = _first - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:16:13 + | +LL | let _ = Instant::now() - second; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index 08aee4332151..cbc6768033ec 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -490,4 +490,23 @@ unsafe impl CrateRoot for () {} // SAFETY: ok unsafe impl CrateRoot for (i32) {} +fn issue_9142() { + // SAFETY: ok + let _ = + // we need this comment to avoid rustfmt putting + // it all on one line + unsafe {}; + + // SAFETY: this is more than one level away, so it should warn + let _ = { + if unsafe { true } { + todo!(); + } else { + let bar = unsafe {}; + todo!(); + bar + } + }; +} + fn main() {} diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 2c466ff5c733..ba4de9806d17 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -263,5 +263,29 @@ LL | unsafe impl CrateRoot for () {} | = help: consider adding a safety comment on the preceding line -error: aborting due to 31 previous errors +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:498:9 + | +LL | unsafe {}; + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:502:12 + | +LL | if unsafe { true } { + | ^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:505:23 + | +LL | let bar = unsafe {}; + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 34 previous errors diff --git a/tests/ui/unnecessary_join.stderr b/tests/ui/unnecessary_join.stderr index 0b14b143affd..e919a6d1d8aa 100644 --- a/tests/ui/unnecessary_join.stderr +++ b/tests/ui/unnecessary_join.stderr @@ -1,4 +1,4 @@ -error: called `.collect>().join("")` on an iterator +error: called `.collect::>().join("")` on an iterator --> $DIR/unnecessary_join.rs:11:10 | LL | .collect::>() @@ -8,7 +8,7 @@ LL | | .join(""); | = note: `-D clippy::unnecessary-join` implied by `-D warnings` -error: called `.collect>().join("")` on an iterator +error: called `.collect::>().join("")` on an iterator --> $DIR/unnecessary_join.rs:20:10 | LL | .collect::>() diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed index 54f85806ac3b..38fe6c34cfec 100644 --- a/tests/ui/unused_rounding.fixed +++ b/tests/ui/unused_rounding.fixed @@ -6,4 +6,9 @@ fn main() { let _ = 1.0f64; let _ = 1.00f32; let _ = 2e-54f64.floor(); + + // issue9866 + let _ = 3.3_f32.round(); + let _ = 3.3_f64.round(); + let _ = 3.0_f32; } diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs index 8d007bc4a1dc..a5cac64d023a 100644 --- a/tests/ui/unused_rounding.rs +++ b/tests/ui/unused_rounding.rs @@ -6,4 +6,9 @@ fn main() { let _ = 1.0f64.floor(); let _ = 1.00f32.round(); let _ = 2e-54f64.floor(); + + // issue9866 + let _ = 3.3_f32.round(); + let _ = 3.3_f64.round(); + let _ = 3.0_f32.round(); } diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr index 6cfb02e04028..1eeb5d1de883 100644 --- a/tests/ui/unused_rounding.stderr +++ b/tests/ui/unused_rounding.stderr @@ -18,5 +18,11 @@ error: used the `round` method with a whole number float LL | let _ = 1.00f32.round(); | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32` -error: aborting due to 3 previous errors +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:13:13 + | +LL | let _ = 3.0_f32.round(); + | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32` + +error: aborting due to 4 previous errors diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 7bb43cf7ae82..3dd640b86f0b 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -7,6 +7,7 @@ // test of the JSON error format. #![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] #![rustfmt::skip] #![deny(clippy::unused_unit)] @@ -87,3 +88,9 @@ fn macro_expr() { } e!() } + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 21073fb802ad..bddecf06fb76 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -7,6 +7,7 @@ // test of the JSON error format. #![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] #![rustfmt::skip] #![deny(clippy::unused_unit)] @@ -87,3 +88,9 @@ fn macro_expr() { } e!() } + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index 0d2cb77855be..ce06738cfe47 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,119 +1,119 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:58 + --> $DIR/unused_unit.rs:20:58 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` | note: the lint level is defined here - --> $DIR/unused_unit.rs:12:9 + --> $DIR/unused_unit.rs:13:9 | LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:28 + --> $DIR/unused_unit.rs:20:28 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:18 + --> $DIR/unused_unit.rs:21:18 | LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:21:26 + --> $DIR/unused_unit.rs:22:26 | LL | let _y: &dyn Fn() -> () = &f; | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:28:18 + --> $DIR/unused_unit.rs:29:18 | LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:29:9 + --> $DIR/unused_unit.rs:30:9 | LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:34:29 + --> $DIR/unused_unit.rs:35:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:36:19 + --> $DIR/unused_unit.rs:37:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:37:16 + --> $DIR/unused_unit.rs:38:16 | LL | H: Fn() -> (); | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:41:29 + --> $DIR/unused_unit.rs:42:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:43:19 + --> $DIR/unused_unit.rs:44:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:44:16 + --> $DIR/unused_unit.rs:45:16 | LL | H: Fn() -> () {} | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:47:17 + --> $DIR/unused_unit.rs:48:17 | LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:47:26 + --> $DIR/unused_unit.rs:48:26 | LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:57:14 + --> $DIR/unused_unit.rs:58:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:59:11 + --> $DIR/unused_unit.rs:60:11 | LL | return(); | ^^ help: remove the `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:76:10 + --> $DIR/unused_unit.rs:77:10 | LL | fn test()->(){} | ^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:79:11 + --> $DIR/unused_unit.rs:80:11 | LL | fn test2() ->(){} | ^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:82:11 + --> $DIR/unused_unit.rs:83:11 | LL | fn test3()-> (){} | ^^^^^ help: remove the `-> ()` diff --git a/tests/ui/unwrap.stderr b/tests/ui/unwrap.stderr index e88d580f7bd2..d49bf2b32283 100644 --- a/tests/ui/unwrap.stderr +++ b/tests/ui/unwrap.stderr @@ -1,4 +1,4 @@ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap.rs:5:13 | LL | let _ = opt.unwrap(); @@ -7,7 +7,7 @@ LL | let _ = opt.unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message = note: `-D clippy::unwrap-used` implied by `-D warnings` -error: used `unwrap()` on `a Result` value +error: used `unwrap()` on a `Result` value --> $DIR/unwrap.rs:10:13 | LL | let _ = res.unwrap(); @@ -15,7 +15,7 @@ LL | let _ = res.unwrap(); | = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message -error: used `unwrap_err()` on `a Result` value +error: used `unwrap_err()` on a `Result` value --> $DIR/unwrap.rs:11:13 | LL | let _ = res.unwrap_err(); diff --git a/tests/ui/unwrap_expect_used.stderr b/tests/ui/unwrap_expect_used.stderr index 211d2be18342..fe4ecef11453 100644 --- a/tests/ui/unwrap_expect_used.stderr +++ b/tests/ui/unwrap_expect_used.stderr @@ -1,4 +1,4 @@ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_expect_used.rs:23:5 | LL | Some(3).unwrap(); @@ -7,7 +7,7 @@ LL | Some(3).unwrap(); = help: if this value is `None`, it will panic = note: `-D clippy::unwrap-used` implied by `-D warnings` -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/unwrap_expect_used.rs:24:5 | LL | Some(3).expect("Hello world!"); @@ -16,7 +16,7 @@ LL | Some(3).expect("Hello world!"); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `unwrap()` on `a Result` value +error: used `unwrap()` on a `Result` value --> $DIR/unwrap_expect_used.rs:31:5 | LL | a.unwrap(); @@ -24,7 +24,7 @@ LL | a.unwrap(); | = help: if this value is an `Err`, it will panic -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/unwrap_expect_used.rs:32:5 | LL | a.expect("Hello world!"); @@ -32,7 +32,7 @@ LL | a.expect("Hello world!"); | = help: if this value is an `Err`, it will panic -error: used `unwrap_err()` on `a Result` value +error: used `unwrap_err()` on a `Result` value --> $DIR/unwrap_expect_used.rs:33:5 | LL | a.unwrap_err(); @@ -40,7 +40,7 @@ LL | a.unwrap_err(); | = help: if this value is an `Ok`, it will panic -error: used `expect_err()` on `a Result` value +error: used `expect_err()` on a `Result` value --> $DIR/unwrap_expect_used.rs:34:5 | LL | a.expect_err("Hello error!"); diff --git a/tests/ui/unwrap_or.rs b/tests/ui/unwrap_or.rs index bfb41e439473..a0c003f5b1ea 100644 --- a/tests/ui/unwrap_or.rs +++ b/tests/ui/unwrap_or.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all)] +#![warn(clippy::all, clippy::or_fun_call)] fn main() { let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); diff --git a/tests/ui/use_self_trait.fixed b/tests/ui/use_self_trait.fixed index 9bcd692fb351..4e779308d024 100644 --- a/tests/ui/use_self_trait.fixed +++ b/tests/ui/use_self_trait.fixed @@ -47,8 +47,7 @@ impl Mul for Bad { impl Clone for Bad { fn clone(&self) -> Self { - // FIXME: applicable here - Bad + Self } } @@ -112,4 +111,42 @@ impl NameTrait for u8 { } } +mod impl_in_macro { + macro_rules! parse_ip_impl { + // minimized from serde=1.0.118 + ($ty:ty) => { + impl FooTrait for $ty { + fn new() -> Self { + <$ty>::bar() + } + } + }; + } + + struct Foo; + + trait FooTrait { + fn new() -> Self; + } + + impl Foo { + fn bar() -> Self { + Self + } + } + parse_ip_impl!(Foo); // Should not lint +} + +mod full_path_replacement { + trait Error { + fn custom(_msg: T) -> Self; + } + + impl Error for std::fmt::Error { + fn custom(_msg: T) -> Self { + Self // Should lint + } + } +} + fn main() {} diff --git a/tests/ui/use_self_trait.rs b/tests/ui/use_self_trait.rs index de305d40f330..325dc73b21ea 100644 --- a/tests/ui/use_self_trait.rs +++ b/tests/ui/use_self_trait.rs @@ -47,7 +47,6 @@ impl Mul for Bad { impl Clone for Bad { fn clone(&self) -> Self { - // FIXME: applicable here Bad } } @@ -112,4 +111,42 @@ impl NameTrait for u8 { } } +mod impl_in_macro { + macro_rules! parse_ip_impl { + // minimized from serde=1.0.118 + ($ty:ty) => { + impl FooTrait for $ty { + fn new() -> Self { + <$ty>::bar() + } + } + }; + } + + struct Foo; + + trait FooTrait { + fn new() -> Self; + } + + impl Foo { + fn bar() -> Self { + Self + } + } + parse_ip_impl!(Foo); // Should not lint +} + +mod full_path_replacement { + trait Error { + fn custom(_msg: T) -> Self; + } + + impl Error for std::fmt::Error { + fn custom(_msg: T) -> Self { + std::fmt::Error // Should lint + } + } +} + fn main() {} diff --git a/tests/ui/use_self_trait.stderr b/tests/ui/use_self_trait.stderr index 55af3ff2a93d..090729b9c3d5 100644 --- a/tests/ui/use_self_trait.stderr +++ b/tests/ui/use_self_trait.stderr @@ -84,5 +84,17 @@ error: unnecessary structure name repetition LL | fn mul(self, rhs: Bad) -> Bad { | ^^^ help: use the applicable keyword: `Self` -error: aborting due to 14 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:50:9 + | +LL | Bad + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:147:13 + | +LL | std::fmt::Error // Should lint + | ^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 16 previous errors diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index c23231a99e9f..871e4fb5c3a9 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![allow(unused)] #![warn(clippy::useless_attribute)] #![warn(unreachable_pub)] #![feature(rustc_private)] @@ -16,6 +17,13 @@ extern crate rustc_middle; #[macro_use] extern crate proc_macro_derive; +fn test_indented_attr() { + #![allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::::default(); +} + // don't lint on unused_import for `use` items #[allow(unused_imports)] use std::collections; @@ -63,13 +71,16 @@ mod c { pub(crate) struct S; } -fn test_indented_attr() { - #![allow(clippy::almost_swapped)] - use std::collections::HashSet; - - let _ = HashSet::::default(); +// https://github.com/rust-lang/rust-clippy/issues/7511 +pub mod split { + #[allow(clippy::module_name_repetitions)] + pub use regex::SplitN; } +// https://github.com/rust-lang/rust-clippy/issues/8768 +#[allow(clippy::single_component_path_imports)] +use regex; + fn main() { test_indented_attr(); } diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 7a7b198ea607..cb50736ba395 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![allow(unused)] #![warn(clippy::useless_attribute)] #![warn(unreachable_pub)] #![feature(rustc_private)] @@ -16,6 +17,13 @@ extern crate rustc_middle; #[macro_use] extern crate proc_macro_derive; +fn test_indented_attr() { + #[allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::::default(); +} + // don't lint on unused_import for `use` items #[allow(unused_imports)] use std::collections; @@ -63,13 +71,16 @@ mod c { pub(crate) struct S; } -fn test_indented_attr() { - #[allow(clippy::almost_swapped)] - use std::collections::HashSet; - - let _ = HashSet::::default(); +// https://github.com/rust-lang/rust-clippy/issues/7511 +pub mod split { + #[allow(clippy::module_name_repetitions)] + pub use regex::SplitN; } +// https://github.com/rust-lang/rust-clippy/issues/8768 +#[allow(clippy::single_component_path_imports)] +use regex; + fn main() { test_indented_attr(); } diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index 255d28763553..a7ea0df22945 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -1,5 +1,5 @@ error: useless lint attribute - --> $DIR/useless_attribute.rs:8:1 + --> $DIR/useless_attribute.rs:9:1 | LL | #[allow(dead_code)] | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]` @@ -7,13 +7,13 @@ LL | #[allow(dead_code)] = note: `-D clippy::useless-attribute` implied by `-D warnings` error: useless lint attribute - --> $DIR/useless_attribute.rs:9:1 + --> $DIR/useless_attribute.rs:10:1 | LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:67:5 + --> $DIR/useless_attribute.rs:21:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index a6d8d0307ce5..7a85386a3df4 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -6,7 +6,7 @@ use rustc_tools_util::VersionInfo; use std::fs; #[test] -fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { +fn consistent_clippy_crate_versions() { fn read_version(path: &str) -> String { let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{path}`: {e:?}")); contents @@ -24,11 +24,16 @@ fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { } let clippy_version = read_version("Cargo.toml"); - let clippy_lints_version = read_version("clippy_lints/Cargo.toml"); - let clippy_utils_version = read_version("clippy_utils/Cargo.toml"); - assert_eq!(clippy_version, clippy_lints_version); - assert_eq!(clippy_version, clippy_utils_version); + let paths = [ + "declare_clippy_lint/Cargo.toml", + "clippy_lints/Cargo.toml", + "clippy_utils/Cargo.toml", + ]; + + for path in paths { + assert_eq!(clippy_version, read_version(path), "{path} version differs"); + } } #[test] From 28fb084ec70844a03f83dbd7a776ca0917b890be Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 20:52:12 +0100 Subject: [PATCH 234/524] Fix declare_clippy_lint crate --- declare_clippy_lint/Cargo.toml | 3 +++ declare_clippy_lint/src/lib.rs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 578109840fb7..082570f1fe5d 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -11,3 +11,6 @@ proc-macro = true itertools = "0.10.1" quote = "1.0.21" syn = "1.0.100" + +[features] +deny-warnings = [] diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs index 962766916dd1..26210556d652 100644 --- a/declare_clippy_lint/src/lib.rs +++ b/declare_clippy_lint/src/lib.rs @@ -1,5 +1,7 @@ #![feature(let_chains)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] +// warn on lints, that are included in `rust-lang/rust`s bootstrap +#![warn(rust_2018_idioms, unused_lifetimes)] use proc_macro::TokenStream; use quote::{format_ident, quote}; @@ -29,7 +31,7 @@ struct ClippyLint { } impl Parse for ClippyLint { - fn parse(input: ParseStream) -> Result { + fn parse(input: ParseStream<'_>) -> Result { let attrs = input.call(Attribute::parse_outer)?; let mut in_code = false; From 6fce4691d523f0d22596c53e6fe420b442894482 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 21 Nov 2022 21:04:59 +0100 Subject: [PATCH 235/524] Clippy: Don't import GenericParamDefKind --- clippy_utils/src/ty.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 897edfc5495f..a26cdf647164 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,9 +13,9 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, IntTy, List, ParamEnv, - Predicate, PredicateKind, ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind, + ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; @@ -1011,7 +1011,7 @@ pub fn make_projection<'tcx>( assoc_item.def_id, substs.len(), generic_count, - params.map(GenericParamDefKind::descr).collect::>(), + params.map(ty::GenericParamDefKind::descr).collect::>(), substs, ); @@ -1022,9 +1022,9 @@ pub fn make_projection<'tcx>( .find(|(_, (param, arg))| { !matches!( (param, arg), - (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) - | (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) - | (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) + (ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) ) }) { @@ -1036,7 +1036,7 @@ pub fn make_projection<'tcx>( idx, param.descr(), arg, - params.map(GenericParamDefKind::descr).collect::>(), + params.map(ty::GenericParamDefKind::descr).collect::>(), substs, ); } From c7828221e3fe88d54be2c2c7d0dda226f6be60c4 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 17 Nov 2022 13:00:35 +0000 Subject: [PATCH 236/524] Allow iterators instead of requiring slices that will get turned into iterators --- clippy_lints/src/bool_assert_comparison.rs | 2 +- clippy_lints/src/dereference.rs | 2 +- clippy_utils/src/ty.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 4bd55c1429c3..82d368bb8bc2 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -59,7 +59,7 @@ fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { ) }) .map_or(false, |assoc_item| { - let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, &[])); + let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, [])); let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj); nty.is_bool() diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 218dbeaddcad..03d865af374a 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1263,7 +1263,7 @@ fn replace_types<'tcx>( let item_def_id = projection_predicate.projection_ty.item_def_id; let assoc_item = cx.tcx.associated_item(item_def_id); let projection = cx.tcx - .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[])); + .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, [])); if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 3a144c2bb223..a1698a61e601 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -79,7 +79,7 @@ pub fn get_associated_type<'tcx>( .associated_items(trait_id) .find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id) .and_then(|assoc| { - let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[])); + let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, [])); cx.tcx.try_normalize_erasing_regions(cx.param_env, proj).ok() }) } From f60e43ee05620e413d5e7dc069b3334ff4d1d0ed Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 18 Nov 2022 19:58:07 +0000 Subject: [PATCH 237/524] Fix clippy's missing substs --- clippy_lints/src/derive.rs | 4 ++-- clippy_lints/src/eta_reduction.rs | 4 +++- clippy_lints/src/methods/unnecessary_to_owned.rs | 4 ++-- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 2 +- clippy_utils/src/ty.rs | 14 +++++++++----- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 102a02138bc8..1d9af7cdbd35 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -466,12 +466,12 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id); - if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]); + if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []); // If all of our fields implement `Eq`, we can implement `Eq` too if adt .all_fields() .map(|f| f.ty(cx.tcx, substs)) - .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])); + .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, [])); then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 7b9786d7e570..ea4e5e052d02 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -119,11 +119,13 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs(); if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc); if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc); + if let ty::Closure(_, substs) = *closure_ty.kind(); then { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait() - && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &[]) + && let args = cx.tcx.erase_late_bound_regions(ty::ClosureSubsts { substs }.sig()).inputs() + && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &args.iter().copied().map(Into::into).collect::>()) && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)) { // Mutable closure is used after current expr; we cannot consume it. diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index c7775313ecd0..375ebc903b40 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -474,7 +474,7 @@ fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: D } /// Returns true if the named method is `ToString::to_string` and it's called on a type that -/// is string-like i.e. implements `AsRef` or `Deref`. +/// is string-like i.e. implements `AsRef` or `Deref`. fn is_to_string_on_string_like<'a>( cx: &LateContext<'_>, call_expr: &'a Expr<'a>, @@ -490,7 +490,7 @@ fn is_to_string_on_string_like<'a>( && let GenericArgKind::Type(ty) = generic_arg.unpack() && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) - && (implements_trait(cx, ty, deref_trait_id, &[cx.tcx.types.str_.into()]) || + && (get_associated_type(cx, ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) || implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) { true } else { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 79aa15b06ef4..eeff15bbfb42 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -185,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { if !ty.is_mutable_ptr(); if !is_copy(cx, ty); if ty.is_sized(cx.tcx, cx.param_env); - if !allowed_traits.iter().any(|&t| implements_trait(cx, ty, t, &[])); + if !allowed_traits.iter().any(|&t| implements_trait_with_env(cx.tcx, cx.param_env, ty, t, [None])); if !implements_borrow_trait; if !all_borrowable_trait; diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index 5c2b96f5b2ce..a022fc156fca 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { let implements_partial_ord = { if let Some(id) = cx.tcx.lang_items().partial_ord_trait() { - implements_trait(cx, ty, id, &[]) + implements_trait(cx, ty, id, &[ty.into()]) } else { return; } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a1698a61e601..a8047fe9e5ea 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -9,7 +9,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{TyCtxtInferExt, type_variable::{TypeVariableOrigin, TypeVariableOriginKind}}; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ @@ -18,7 +18,7 @@ use rustc_middle::ty::{ }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Size, VariantIdx}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::normalize::AtExt; @@ -153,7 +153,7 @@ pub fn implements_trait<'tcx>( trait_id: DefId, ty_params: &[GenericArg<'tcx>], ) -> bool { - implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params) + implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params.iter().map(|&arg| Some(arg))) } /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. @@ -162,7 +162,7 @@ pub fn implements_trait_with_env<'tcx>( param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, trait_id: DefId, - ty_params: &[GenericArg<'tcx>], + ty_params: impl IntoIterator>>, ) -> bool { // Clippy shouldn't have infer types assert!(!ty.needs_infer()); @@ -171,8 +171,12 @@ pub fn implements_trait_with_env<'tcx>( if ty.has_escaping_bound_vars() { return false; } - let ty_params = tcx.mk_substs(ty_params.iter()); let infcx = tcx.infer_ctxt().build(); + let orig = TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }; + let ty_params = tcx.mk_substs(ty_params.into_iter().map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into()))); infcx .type_implements_trait(trait_id, ty, ty_params, param_env) .must_apply_modulo_regions() From 595ae838558f767a4d54b1c69843f6c53314ebc9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 21 Nov 2022 12:24:53 +0000 Subject: [PATCH 238/524] Stop passing the self-type as a separate argument. --- clippy_lints/src/dereference.rs | 12 ++++-------- clippy_lints/src/ptr.rs | 4 ++-- clippy_utils/src/ty.rs | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 03d865af374a..c4e7f8bfe1e2 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -842,14 +842,10 @@ fn walk_parents<'tcx>( } else if let Some(trait_id) = cx.tcx.trait_of_item(id) && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e)) && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() - && let subs = match cx + && let subs = cx .typeck_results() - .node_substs_opt(parent.hir_id) - .and_then(|subs| subs.get(1..)) - { - Some(subs) => cx.tcx.mk_substs(subs.iter().copied()), - None => cx.tcx.mk_substs(std::iter::empty::>()), - } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() { + .node_substs_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default() + && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() { // Trait methods taking `&self` sub_ty } else { @@ -858,7 +854,7 @@ fn walk_parents<'tcx>( } && impl_ty.is_ref() && let infcx = cx.tcx.infer_ctxt().build() && infcx - .type_implements_trait(trait_id, impl_ty, subs, cx.param_env) + .type_implements_trait(trait_id, [impl_ty.into()].into_iter().chain(subs.iter().copied()), cx.param_env) .must_apply_modulo_regions() { return Some(Position::MethodReceiverRefImpl) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index ea00650d42aa..5420a0e782ea 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -692,7 +692,7 @@ fn matches_preds<'tcx>( let infcx = cx.tcx.infer_ctxt().build(); preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) { ExistentialPredicate::Trait(p) => infcx - .type_implements_trait(p.def_id, ty, p.substs, cx.param_env) + .type_implements_trait(p.def_id, [ty.into()].into_iter().chain(p.substs.iter()), cx.param_env) .must_apply_modulo_regions(), ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new( cx.tcx, @@ -704,7 +704,7 @@ fn matches_preds<'tcx>( )), )), ExistentialPredicate::AutoTrait(p) => infcx - .type_implements_trait(p, ty, List::empty(), cx.param_env) + .type_implements_trait(p, [ty], cx.param_env) .must_apply_modulo_regions(), }) } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a8047fe9e5ea..5ec6f29fe916 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -178,7 +178,7 @@ pub fn implements_trait_with_env<'tcx>( }; let ty_params = tcx.mk_substs(ty_params.into_iter().map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into()))); infcx - .type_implements_trait(trait_id, ty, ty_params, param_env) + .type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env) .must_apply_modulo_regions() } From eb850aef96ed1914a82feb1e297a6c9f5b71cae3 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 21 Nov 2022 16:52:01 +0100 Subject: [PATCH 239/524] Use `as_closure` helper method Co-authored-by: lcnr --- clippy_lints/src/eta_reduction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index ea4e5e052d02..f34cbee03558 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait() - && let args = cx.tcx.erase_late_bound_regions(ty::ClosureSubsts { substs }.sig()).inputs() + && let args = cx.tcx.erase_late_bound_regions(substs.as_closure().sig()).inputs() && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &args.iter().copied().map(Into::into).collect::>()) && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)) { From 48b10feedbee5b8554fa82696ce5e836933f189c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 18 Nov 2022 11:24:21 +1100 Subject: [PATCH 240/524] Split `MacArgs` in two. `MacArgs` is an enum with three variants: `Empty`, `Delimited`, and `Eq`. It's used in two ways: - For representing attribute macro arguments (e.g. in `AttrItem`), where all three variants are used. - For representing function-like macros (e.g. in `MacCall` and `MacroDef`), where only the `Delimited` variant is used. In other words, `MacArgs` is used in two quite different places due to them having partial overlap. I find this makes the code hard to read. It also leads to various unreachable code paths, and allows invalid values (such as accidentally using `MacArgs::Empty` in a `MacCall`). This commit splits `MacArgs` in two: - `DelimArgs` is a new struct just for the "delimited arguments" case. It is now used in `MacCall` and `MacroDef`. - `AttrArgs` is a renaming of the old `MacArgs` enum for the attribute macro case. Its `Delimited` variant now contains a `DelimArgs`. Various other related things are renamed as well. These changes make the code clearer, avoids several unreachable paths, and disallows the invalid values. --- clippy_lints/src/crate_in_macro_def.rs | 2 +- clippy_utils/src/ast_utils.rs | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index 20cc330e035f..b2fe0386f945 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -55,7 +55,7 @@ impl EarlyLintPass for CrateInMacroDef { if_chain! { if item.attrs.iter().any(is_macro_export); if let ItemKind::MacroDef(macro_def) = &item.kind; - let tts = macro_def.body.inner_tokens(); + let tts = macro_def.body.tokens.clone(); if let Some(span) = contains_unhygienic_crate_reference(&tts); then { span_lint_and_sugg( diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 23aed4b5ba2f..87b378bfd198 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -388,7 +388,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) }, (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body), + (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_delim_args(&l.body, &r.body), _ => false, } } @@ -709,7 +709,7 @@ pub fn eq_assoc_constraint(l: &AssocConstraint, r: &AssocConstraint) -> bool { } pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool { - eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args) + eq_path(&l.path, &r.path) && eq_delim_args(&l.args, &r.args) } pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool { @@ -717,18 +717,22 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool { l.style == r.style && match (&l.kind, &r.kind) { (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2, - (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_mac_args(&l.item.args, &r.item.args), + (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_args(&l.item.args, &r.item.args), _ => false, } } -pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool { - use MacArgs::*; +pub fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool { + use AttrArgs::*; match (l, r) { (Empty, Empty) => true, - (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts), - (Eq(_, MacArgsEq::Ast(le)), Eq(_, MacArgsEq::Ast(re))) => eq_expr(le, re), - (Eq(_, MacArgsEq::Hir(ll)), Eq(_, MacArgsEq::Hir(rl))) => ll.kind == rl.kind, + (Delimited(la), Delimited(ra)) => eq_delim_args(la, ra), + (Eq(_, AttrArgsEq::Ast(le)), Eq(_, AttrArgsEq::Ast(re))) => eq_expr(le, re), + (Eq(_, AttrArgsEq::Hir(ll)), Eq(_, AttrArgsEq::Hir(rl))) => ll.kind == rl.kind, _ => false, } } + +pub fn eq_delim_args(l: &DelimArgs, r: &DelimArgs) -> bool { + l.delim == r.delim && l.tokens.eq_unspanned(&r.tokens) +} From aa66212e291cf6d726d58d139b7ab19631431bd5 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 21 Nov 2022 22:55:14 +0100 Subject: [PATCH 241/524] Cleanup `rustc_tool_util` and add a convenient macro for `build.rs` --- Cargo.toml | 4 ++-- build.rs | 14 +------------- rustc_tools_util/README.md | 31 ++++++++++++------------------- rustc_tools_util/src/lib.rs | 36 ++++++++++++++++++++++++++++-------- src/driver.rs | 1 - src/main.rs | 1 - tests/versioncheck.rs | 1 - 7 files changed, 43 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6bdac84ada00..698ff035a861 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ path = "src/driver.rs" [dependencies] clippy_lints = { path = "clippy_lints" } semver = "1.0" -rustc_tools_util = "0.2.1" +rustc_tools_util = {version = "0.2.1", path = "./rustc_tools_util"} tempfile = { version = "3.2", optional = true } termize = "0.1" @@ -56,7 +56,7 @@ tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" [build-dependencies] -rustc_tools_util = "0.2.1" +rustc_tools_util = {version = "0.2.1", path = "./rustc_tools_util"} [features] deny-warnings = ["clippy_lints/deny-warnings"] diff --git a/build.rs b/build.rs index b5484bec3c8b..b79d09b0dd2d 100644 --- a/build.rs +++ b/build.rs @@ -3,17 +3,5 @@ fn main() { println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap()); // Don't rebuild even if nothing changed println!("cargo:rerun-if-changed=build.rs"); - // forward git repo hashes we build at - println!( - "cargo:rustc-env=GIT_HASH={}", - rustc_tools_util::get_commit_hash().unwrap_or_default() - ); - println!( - "cargo:rustc-env=COMMIT_DATE={}", - rustc_tools_util::get_commit_date().unwrap_or_default() - ); - println!( - "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", - rustc_tools_util::get_channel() - ); + rustc_tools_util::setup_version_info!(); } diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index e947f9c7e66e..6204ca174f35 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -20,36 +20,29 @@ rustc_tools_util = "0.2.1" ```` In `build.rs`, generate the data in your `main()` -````rust + +```rust fn main() { - println!( - "cargo:rustc-env=GIT_HASH={}", - rustc_tools_util::get_commit_hash().unwrap_or_default() - ); - println!( - "cargo:rustc-env=COMMIT_DATE={}", - rustc_tools_util::get_commit_date().unwrap_or_default() - ); - println!( - "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", - rustc_tools_util::get_channel().unwrap_or_default() - ); + rustc_tools_util::setup_version_info!(); } - -```` +``` Use the version information in your main.rs -````rust -use rustc_tools_util::*; +```rust fn show_version() { let version_info = rustc_tools_util::get_version_info!(); println!("{}", version_info); } -```` +``` + This gives the following output in clippy: -`clippy 0.0.212 (a416c5e 2018-12-14)` +`clippy 0.1.66 (a28f3c8 2022-11-20)` + +## Repository +This project is part of the rust-lang/rust-clippy repository. The source code +can be found under `./rustc_tools_util/`. ## License diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index 01d25c53126f..5e856319c886 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -2,19 +2,21 @@ use std::env; +/// This macro creates the version string during compilation from the +/// current environment #[macro_export] macro_rules! get_version_info { () => {{ - let major = env!("CARGO_PKG_VERSION_MAJOR").parse::().unwrap(); - let minor = env!("CARGO_PKG_VERSION_MINOR").parse::().unwrap(); - let patch = env!("CARGO_PKG_VERSION_PATCH").parse::().unwrap(); - let crate_name = String::from(env!("CARGO_PKG_NAME")); + let major = std::env!("CARGO_PKG_VERSION_MAJOR").parse::().unwrap(); + let minor = std::env!("CARGO_PKG_VERSION_MINOR").parse::().unwrap(); + let patch = std::env!("CARGO_PKG_VERSION_PATCH").parse::().unwrap(); + let crate_name = String::from(std::env!("CARGO_PKG_NAME")); - let host_compiler = option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string); - let commit_hash = option_env!("GIT_HASH").map(str::to_string); - let commit_date = option_env!("COMMIT_DATE").map(str::to_string); + let host_compiler = std::option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string); + let commit_hash = std::option_env!("GIT_HASH").map(str::to_string); + let commit_date = std::option_env!("COMMIT_DATE").map(str::to_string); - VersionInfo { + $crate::VersionInfo { major, minor, patch, @@ -26,6 +28,24 @@ macro_rules! get_version_info { }}; } +/// This macro can be used in `build.rs` to automatically set the needed +/// environment values, namely `GIT_HASH`, `COMMIT_DATE` and +/// `RUSTC_RELEASE_CHANNEL` +#[macro_export] +macro_rules! setup_version_info { + () => {{ + println!( + "cargo:rustc-env=GIT_HASH={}", + $crate::get_commit_hash().unwrap_or_default() + ); + println!( + "cargo:rustc-env=COMMIT_DATE={}", + $crate::get_commit_date().unwrap_or_default() + ); + println!("cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", $crate::get_channel()); + }}; +} + // some code taken and adapted from RLS and cargo pub struct VersionInfo { pub major: u8, diff --git a/src/driver.rs b/src/driver.rs index ee2a3ad20d3e..0aa7d437b4da 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -18,7 +18,6 @@ extern crate rustc_span; use rustc_interface::interface; use rustc_session::parse::ParseSess; use rustc_span::symbol::Symbol; -use rustc_tools_util::VersionInfo; use std::borrow::Cow; use std::env; diff --git a/src/main.rs b/src/main.rs index d418d2daa313..7a78b32620d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use rustc_tools_util::VersionInfo; use std::env; use std::path::PathBuf; use std::process::{self, Command}; diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 7a85386a3df4..c721e9969c9a 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -2,7 +2,6 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::single_match_else)] -use rustc_tools_util::VersionInfo; use std::fs; #[test] From e95d40980b3044a6a9cad1b5e72eb57390790d18 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 22 Nov 2022 14:30:29 +0100 Subject: [PATCH 242/524] Clippy: Workaround for let_chains issue --- src/driver.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index ee2a3ad20d3e..ad6132a49baa 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -90,11 +90,12 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option) { // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever // it is rebuilt - if cfg!(debug_assertions) - && let Ok(current_exe) = env::current_exe() - && let Some(current_exe) = current_exe.to_str() - { - file_depinfo.insert(Symbol::intern(current_exe)); + if cfg!(debug_assertions) { + if let Ok(current_exe) = env::current_exe() + && let Some(current_exe) = current_exe.to_str() + { + file_depinfo.insert(Symbol::intern(current_exe)); + } } } From 2cda73f617028712c097124f8f38ace4660c066d Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 22 Nov 2022 13:47:38 -0500 Subject: [PATCH 243/524] Use `walk_generic_arg` --- clippy_lints/src/lifetimes.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index d9acaa99c6d1..897428797e57 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -3,7 +3,7 @@ use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; use rustc_hir::lang_items; @@ -503,14 +503,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { { self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.span); } - // Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands. - // walk_generic_arg(self, generic_arg); - match generic_arg { - GenericArg::Lifetime(lt) => self.visit_lifetime(lt), - GenericArg::Type(ty) => self.visit_ty(ty), - GenericArg::Const(ct) => self.visit_anon_const(&ct.value), - GenericArg::Infer(inf) => self.visit_infer(inf), - } + walk_generic_arg(self, generic_arg); } } From 284ce9ed0d18b4582e1d377a501355fa685300fd Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 22 Nov 2022 19:52:46 +0000 Subject: [PATCH 244/524] Move `get_associated_type` from `clippy` to `rustc_lint` --- .../src/methods/iter_overeager_cloned.rs | 4 +-- .../src/methods/unnecessary_iter_cloned.rs | 4 +-- .../src/methods/unnecessary_to_owned.rs | 27 ++++++++++++------- clippy_utils/src/ty.rs | 19 +------------ 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index 06a39c5997e2..b4210d875104 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{get_associated_type, implements_trait, is_copy}; +use clippy_utils::ty::{implements_trait, is_copy}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>( && let Some(method_id) = typeck.type_dependent_def_id(cloned_call.hir_id) && cx.tcx.trait_of_item(method_id) == Some(iter_id) && let cloned_recv_ty = typeck.expr_ty_adjusted(cloned_recv) - && let Some(iter_assoc_ty) = get_associated_type(cx, cloned_recv_ty, iter_id, "Item") + && let Some(iter_assoc_ty) = cx.get_associated_type(cloned_recv_ty, iter_id, "Item") && matches!(*iter_assoc_ty.kind(), ty::Ref(_, ty, _) if !is_copy(cx, ty)) { if needs_into_iter diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 4eb579af7a12..52a4ff7d1ae4 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::{fn_def_id, get_parent_expr}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, Expr, ExprKind}; @@ -54,7 +54,7 @@ pub fn check_for_loop_iter( if let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator); let collection_ty = cx.typeck_results().expr_ty(collection); if implements_trait(cx, collection_ty, into_iterator_trait_id, &[]); - if let Some(into_iter_item_ty) = get_associated_type(cx, collection_ty, into_iterator_trait_id, "Item"); + if let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item"); if iter_item_ty == into_iter_item_ty; if let Some(collection_snippet) = snippet_opt(cx, collection.span); diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 375ebc903b40..8b000cd754cd 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -2,9 +2,11 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; +use clippy_utils::{ + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty, +}; use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node}; @@ -18,7 +20,9 @@ use rustc_middle::ty::EarlyBinder; use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; use rustc_semver::RustcVersion; use rustc_span::{sym, Symbol}; -use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; +use rustc_trait_selection::traits::{ + query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause, +}; use std::cmp::max; use super::UNNECESSARY_TO_OWNED; @@ -146,7 +150,7 @@ fn check_addr_of_expr( if_chain! { if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref); if implements_trait(cx, receiver_ty, deref_trait_id, &[]); - if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty); + if cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty); then { if n_receiver_refs > 0 { span_lint_and_sugg( @@ -341,13 +345,13 @@ fn get_input_traits_and_projections<'tcx>( if trait_predicate.trait_ref.self_ty() == input { trait_predicates.push(trait_predicate); } - }, + } PredicateKind::Projection(projection_predicate) => { if projection_predicate.projection_ty.self_ty() == input { projection_predicates.push(projection_predicate); } - }, - _ => {}, + } + _ => {} } } (trait_predicates, projection_predicates) @@ -462,7 +466,12 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. -fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { +fn is_to_owned_like<'a>( + cx: &LateContext<'a>, + call_expr: &Expr<'a>, + method_name: Symbol, + method_def_id: DefId, +) -> bool { is_clone_like(cx, method_name.as_str(), method_def_id) || is_cow_into_owned(cx, method_name, method_def_id) || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) @@ -490,7 +499,7 @@ fn is_to_string_on_string_like<'a>( && let GenericArgKind::Type(ty) = generic_arg.unpack() && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) - && (get_associated_type(cx, ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) || + && (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) || implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) { true } else { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 1b65b701409e..8284dc5c28c0 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -117,24 +117,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { cx.tcx .get_diagnostic_item(sym::Iterator) - .and_then(|iter_did| get_associated_type(cx, ty, iter_did, "Item")) -} - -/// Returns the associated type `name` for `ty` as an implementation of `trait_id`. -/// Do not invoke without first verifying that the type implements the trait. -pub fn get_associated_type<'tcx>( - cx: &LateContext<'tcx>, - ty: Ty<'tcx>, - trait_id: DefId, - name: &str, -) -> Option> { - cx.tcx - .associated_items(trait_id) - .find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id) - .and_then(|assoc| { - let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, [])); - cx.tcx.try_normalize_erasing_regions(cx.param_env, proj).ok() - }) + .and_then(|iter_did| cx.get_associated_type(ty, iter_did, "Item")) } /// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type From 93cfcedfd5582ef4d2966d39eb32d2aa3e3f2dee Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 5 Nov 2022 22:41:07 +0000 Subject: [PATCH 245/524] Separate lifetime ident from resolution in HIR. --- clippy_lints/src/lifetimes.rs | 26 ++++++++++---------------- clippy_lints/src/manual_async_fn.rs | 4 ++-- clippy_lints/src/ptr.rs | 19 +++++++------------ clippy_lints/src/types/borrowed_box.rs | 4 ++-- clippy_utils/src/hir_utils.rs | 16 +++++----------- 5 files changed, 26 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 0bb9eca15287..5df8b486f332 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -10,7 +10,7 @@ use rustc_hir::lang_items; use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, + ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; @@ -180,7 +180,7 @@ fn check_fn_inner<'tcx>( _ => None, }); for bound in lifetimes { - if bound.name != LifetimeName::Static && !bound.is_elided() { + if !bound.is_static() && !bound.is_elided() { return; } } @@ -414,17 +414,13 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { fn record(&mut self, lifetime: &Option) { if let Some(ref lt) = *lifetime { - if lt.name == LifetimeName::Static { + if lt.is_static() { self.lts.push(RefLt::Static); - } else if let LifetimeName::Param(_, ParamName::Fresh) = lt.name { + } else if lt.is_anonymous() { // Fresh lifetimes generated should be ignored. self.lts.push(RefLt::Unnamed); - } else if lt.is_elided() { - self.lts.push(RefLt::Unnamed); - } else if let LifetimeName::Param(def_id, _) = lt.name { + } else if let LifetimeName::Param(def_id) = lt.res { self.lts.push(RefLt::Named(def_id)); - } else { - self.lts.push(RefLt::Unnamed); } } else { self.lts.push(RefLt::Unnamed); @@ -472,7 +468,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { walk_item(self, item); self.lts.truncate(len); self.lts.extend(bounds.iter().filter_map(|bound| match bound { - GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id, _) = l.name { + GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id) = l.res { RefLt::Named(def_id) } else { RefLt::Unnamed @@ -498,10 +494,8 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { } fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { - if let GenericArg::Lifetime(l) = generic_arg - && let LifetimeName::Param(def_id, _) = l.name - { - self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.span); + if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res { + self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span); } // Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands. // walk_generic_arg(self, generic_arg); @@ -577,7 +571,7 @@ where // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - self.map.remove(&lifetime.name.ident().name); + self.map.remove(&lifetime.ident.name); } fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) { @@ -653,7 +647,7 @@ struct BodyLifetimeChecker { impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - if lifetime.name.ident().name != kw::UnderscoreLifetime && lifetime.name.ident().name != kw::StaticLifetime { + if lifetime.ident.name != kw::UnderscoreLifetime && lifetime.ident.name != kw::StaticLifetime { self.lifetimes_used_in_body = true; } } diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 5c6a342b3d07..553980ebf797 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -118,7 +118,7 @@ fn future_trait_ref<'tcx>( .iter() .filter_map(|bound| { if let GenericArg::Lifetime(lt) = bound { - Some(lt.name) + Some(lt.res) } else { None } @@ -153,7 +153,7 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) .iter() .filter_map(|ty| { if let TyKind::Rptr(lt, _) = ty.kind { - Some(lt.name) + Some(lt.res) } else { None } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 5420a0e782ea..ab960edb7576 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -12,8 +12,8 @@ use rustc_hir::hir_id::HirIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{ self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, - ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, - TraitItem, TraitItemKind, TyKind, Unsafety, + ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, + TyKind, Unsafety, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{Obligation, ObligationCause}; @@ -343,21 +343,16 @@ impl PtrArg<'_> { } struct RefPrefix { - lt: LifetimeName, + lt: Lifetime, mutability: Mutability, } impl fmt::Display for RefPrefix { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use fmt::Write; f.write_char('&')?; - match self.lt { - LifetimeName::Param(_, ParamName::Plain(name)) => { - name.fmt(f)?; - f.write_char(' ')?; - }, - LifetimeName::Infer => f.write_str("'_ ")?, - LifetimeName::Static => f.write_str("'static ")?, - _ => (), + if !self.lt.is_anonymous() { + self.lt.ident.fmt(f)?; + f.write_char(' ')?; } f.write_str(self.mutability.prefix_str()) } @@ -495,7 +490,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( ty_name: name.ident.name, method_renames, ref_prefix: RefPrefix { - lt: lt.name, + lt: lt.clone(), mutability, }, deref_ty, diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 9c6629958401..65dfe7637ea9 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -31,10 +31,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m return false; } - let ltopt = if lt.name.is_anonymous() { + let ltopt = if lt.is_anonymous() { String::new() } else { - format!("{} ", lt.name.ident().as_str()) + format!("{} ", lt.ident.as_str()) }; if mut_ty.mutbl == Mutability::Mut { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 0231a51adf48..48982517751e 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -7,7 +7,7 @@ use rustc_hir::def::Res; use rustc_hir::HirIdMap; use rustc_hir::{ ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, - GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, + GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lexer::{tokenize, TokenKind}; @@ -337,7 +337,7 @@ impl HirEqInterExpr<'_, '_, '_> { } fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool { - left.name == right.name + left.res == right.res } fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool { @@ -925,16 +925,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } pub fn hash_lifetime(&mut self, lifetime: &Lifetime) { - std::mem::discriminant(&lifetime.name).hash(&mut self.s); - if let LifetimeName::Param(param_id, ref name) = lifetime.name { - std::mem::discriminant(name).hash(&mut self.s); + lifetime.ident.name.hash(&mut self.s); + std::mem::discriminant(&lifetime.res).hash(&mut self.s); + if let LifetimeName::Param(param_id) = lifetime.res { param_id.hash(&mut self.s); - match name { - ParamName::Plain(ref ident) => { - ident.name.hash(&mut self.s); - }, - ParamName::Fresh | ParamName::Error => {}, - } } } From 2a530dce53b93ff8f880ffa3e90d5fc53668ba89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 3 Nov 2022 09:19:23 -0700 Subject: [PATCH 246/524] Fix clippy code --- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_utils/src/ast_utils.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 78e83880e1a6..e111c7d22915 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -582,7 +582,7 @@ fn ident_difference_expr_with_base_location( | (Block(_, _), Block(_, _)) | (Closure(_), Closure(_)) | (Match(_, _), Match(_, _)) - | (Loop(_, _), Loop(_, _)) + | (Loop(_, _, _), Loop(_, _, _)) | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) | (While(_, _, _), While(_, _, _)) | (If(_, _, _), If(_, _, _)) diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 5c595f74eff2..6bcf0bbd7eb7 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -171,7 +171,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) }, - (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt), + (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll, rl) && eq_block(lt, rt), (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb), (TryBlock(l), TryBlock(r)) => eq_block(l, r), (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), From 1ebdcca8b97feca67ce077a2bad87e7655bc774a Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 18 Nov 2022 22:56:22 +0100 Subject: [PATCH 247/524] Avoid `GenFuture` shim when compiling async constructs Previously, async constructs would be lowered to "normal" generators, with an additional `from_generator` / `GenFuture` shim in between to convert from `Generator` to `Future`. The compiler will now special-case these generators internally so that async constructs will *directly* implement `Future` without the need to go through the `from_generator` / `GenFuture` shim. The primary motivation for this change was hiding this implementation detail in stack traces and debuginfo, but it can in theory also help the optimizer as there is less abstractions to see through. --- clippy_lints/src/doc.rs | 4 +--- clippy_lints/src/manual_async_fn.rs | 2 +- tests/ui/author/blocks.stdout | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 4557e4328854..ae5f9424b232 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -427,9 +427,7 @@ fn lint_for_missing_headers( let body = cx.tcx.hir().body(body_id); let ret_ty = typeck.expr_ty(body.value); if implements_trait(cx, ret_ty, future, &[]); - if let ty::Opaque(_, subs) = ret_ty.kind(); - if let Some(gen) = subs.types().next(); - if let ty::Generator(_, subs, _) = gen.kind(); + if let ty::Generator(_, subs, _) = ret_ty.kind(); if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result); then { span_lint( diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 5c6a342b3d07..6a98df499125 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -177,7 +177,7 @@ fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) if let Some(args) = cx .tcx .lang_items() - .from_generator_fn() + .identity_future_fn() .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id)); if args.len() == 1; if let Expr { diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index 9de0550d81d0..c6acf24c21ec 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -45,7 +45,7 @@ if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kin && expr1 = &cx.tcx.hir().body(body_id).value && let ExprKind::Call(func, args) = expr1.kind && let ExprKind::Path(ref qpath) = func.kind - && matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)) + && matches!(qpath, QPath::LangItem(LangItem::IdentityFuture, _)) && args.len() == 1 && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind && let FnRetTy::DefaultReturn(_) = fn_decl1.output From 0aaea40eb2015a109d0be49c0d08f6435c6ce437 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Tue, 1 Nov 2022 19:50:30 +0330 Subject: [PATCH 248/524] move some layout logic to rustc_target::abi::layout --- clippy_lints/src/casts/cast_possible_truncation.rs | 5 ++--- clippy_lints/src/lib.rs | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 88deb4565eb2..adbcfd3189b7 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -2,12 +2,11 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::expr_or_init; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; -use rustc_ast::ast; -use rustc_attr::IntType; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_target::abi::IntegerType; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; @@ -122,7 +121,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, let cast_from_ptr_size = def.repr().int.map_or(true, |ty| { matches!( ty, - IntType::SignedInt(ast::IntTy::Isize) | IntType::UnsignedInt(ast::UintTy::Usize) + IntegerType::Pointer(_), ) }); let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b481314abedc..601990cd6a31 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -26,7 +26,6 @@ extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; From 42db5e5e6275fdd563e6975bf29d675ea47d37e5 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 6 Nov 2022 11:40:31 +0000 Subject: [PATCH 249/524] Use kw::Empty for elided lifetimes in path. --- clippy_lints/src/lifetimes.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 5df8b486f332..220941dcd5db 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -10,8 +10,8 @@ use rustc_hir::lang_items; use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, - TraitItemKind, Ty, TyKind, WherePredicate, + ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, PolyTraitRef, PredicateOrigin, TraitFn, + TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter as middle_nested_filter; @@ -595,7 +595,9 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, .params .iter() .filter_map(|par| match par.kind { - GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)), + GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit, + } => Some((par.name.ident().name, par.span)), _ => None, }) .collect(); @@ -620,7 +622,9 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' .params .iter() .filter_map(|par| match par.kind { - GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)), + GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit, + } => Some((par.name.ident().name, par.span)), _ => None, }) .collect(); @@ -647,7 +651,7 @@ struct BodyLifetimeChecker { impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - if lifetime.ident.name != kw::UnderscoreLifetime && lifetime.ident.name != kw::StaticLifetime { + if !lifetime.is_anonymous() && lifetime.ident.name != kw::StaticLifetime { self.lifetimes_used_in_body = true; } } From 4d0fb089217067b1c7ec21205303e8fd95f60fbc Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 24 Nov 2022 21:33:25 +0000 Subject: [PATCH 250/524] Fix remark for `rfcs/0001-syntax-tree-patterns.md` --- rfcs/0001-syntax-tree-patterns.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/rfcs/0001-syntax-tree-patterns.md b/rfcs/0001-syntax-tree-patterns.md index 8d285b2ef44c..9161986a7b77 100644 --- a/rfcs/0001-syntax-tree-patterns.md +++ b/rfcs/0001-syntax-tree-patterns.md @@ -1,3 +1,5 @@ + + - Feature Name: syntax-tree-patterns - Start Date: 2019-03-12 - RFC PR: (leave this empty) @@ -6,13 +8,11 @@ > Note: This project is part of my Master's Thesis (supervised by [@oli-obk](https://github.com/oli-obk)) # Summary -[summary]: #summary Introduce a domain-specific language (similar to regular expressions) that allows to describe lints using *syntax tree patterns*. # Motivation -[motivation]: #motivation Finding parts of a syntax tree (AST, HIR, ...) that have certain properties (e.g. "*an if that has a block as its condition*") is a major task when writing lints. For non-trivial lints, it often requires nested pattern matching of AST / HIR nodes. For example, testing that an expression is a boolean literal requires the following checks: @@ -68,7 +68,6 @@ A lot of complexity in writing lints currently seems to come from having to manu While regular expressions are very useful when searching for patterns in flat character sequences, they cannot easily be applied to hierarchical data structures like syntax trees. This RFC therefore proposes a pattern matching system that is inspired by regular expressions and designed for hierarchical syntax trees. # Guide-level explanation -[guide-level-explanation]: #guide-level-explanation This proposal adds a `pattern!` macro that can be used to specify a syntax tree pattern to search for. A simple pattern is shown below: @@ -281,7 +280,6 @@ The following table gives an summary of the pattern syntax: ## The result type -[the-result-type]: #the-result-type A lot of lints require checks that go beyond what the pattern syntax described above can express. For example, a lint might want to check whether a node was created as part of a macro expansion or whether there's no comment above a node. Another example would be a lint that wants to match two nodes that have the same value (as needed by lints like `almost_swapped`). Instead of allowing users to write these checks into the pattern directly (which might make patterns hard to read), the proposed solution allows users to assign names to parts of a pattern expression. When matching a pattern against a syntax tree node, the return value will contain references to all nodes that were matched by these named subpatterns. This is similar to capture groups in regular expressions. @@ -372,7 +370,6 @@ As a "real-world" example, I re-implemented the `collapsible_if` lint using patt # Reference-level explanation -[reference-level-explanation]: #reference-level-explanation ## Overview @@ -517,7 +514,6 @@ All `IsMatch` implementations for matching the current *PatternTree* against `sy # Drawbacks -[drawbacks]: #drawbacks #### Performance @@ -571,7 +567,6 @@ Even though I'd expect that a lot of lints can be written using the proposed pat # Rationale and alternatives -[rationale-and-alternatives]: #rationale-and-alternatives Specifying lints using syntax tree patterns has a couple of advantages compared to the current approach of manually writing matching code. First, syntax tree patterns allow users to describe patterns in a simple and expressive way. This makes it easier to write new lints for both novices and experts and also makes reading / modifying existing lints simpler. @@ -632,14 +627,12 @@ The issue of users not knowing about the *PatternTree* structure could be solved For some simple cases (like the first example above), it might be possible to successfully mix Rust and pattern syntax. This space could be further explored in a future extension. # Prior art -[prior-art]: #prior-art The pattern syntax is heavily inspired by regular expressions (repetitions, alternatives, sequences, ...). From what I've seen until now, other linters also implement lints that directly work on syntax tree data structures, just like clippy does currently. I would therefore consider the pattern syntax to be *new*, but please correct me if I'm wrong. # Unresolved questions -[unresolved-questions]: #unresolved-questions #### How to handle multiple matches? @@ -657,7 +650,6 @@ This pattern matches arrays that end with at least one literal. Now given the ar I haven't looked much into this yet because I don't know how relevant it is for most lints. The current implementation simply returns the first match it finds. # Future possibilities -[future-possibilities]: #future-possibilities #### Implement rest of Rust Syntax From 53f78ae0f3ee454b5178aaaedf620d961dbbbb25 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 18 Nov 2022 21:29:26 +0000 Subject: [PATCH 251/524] Simplify a bunch of trait ref obligation creations --- clippy_lints/src/ptr.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 5420a0e782ea..8c4cff66f554 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -698,9 +698,8 @@ fn matches_preds<'tcx>( cx.tcx, ObligationCause::dummy(), cx.param_env, - cx.tcx.mk_predicate(Binder::bind_with_vars( + cx.tcx.mk_predicate(Binder::dummy( PredicateKind::Projection(p.with_self_ty(cx.tcx, ty)), - List::empty(), )), )), ExistentialPredicate::AutoTrait(p) => infcx From 3f059a49a483c3ae31e378e5d5993b4215ee9ce9 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 24 Nov 2022 18:14:58 -0300 Subject: [PATCH 252/524] Introduce PredicateKind::Clause --- clippy_lints/src/dereference.rs | 8 ++++---- clippy_lints/src/derive.rs | 8 ++++---- clippy_lints/src/future_not_send.rs | 4 ++-- clippy_lints/src/methods/unnecessary_to_owned.rs | 8 ++++---- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/ptr.rs | 4 ++-- clippy_lints/src/unit_return_expecting_ord.rs | 6 +++--- clippy_utils/src/eager_or_lazy.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 8 ++++---- clippy_utils/src/ty.rs | 16 ++++++++-------- 10 files changed, 33 insertions(+), 33 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 18e742a85b34..47ea98956be2 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -25,7 +25,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ - self, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, + self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults, }; use rustc_semver::RustcVersion; @@ -1097,7 +1097,7 @@ fn needless_borrow_impl_arg_position<'tcx>( let projection_predicates = predicates .iter() .filter_map(|predicate| { - if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { + if let PredicateKind::Clause(Clause::Projection(projection_predicate)) = predicate.kind().skip_binder() { Some(projection_predicate) } else { None @@ -1111,7 +1111,7 @@ fn needless_borrow_impl_arg_position<'tcx>( if predicates .iter() .filter_map(|predicate| { - if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() + if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) { Some(trait_predicate.trait_ref.def_id) @@ -1173,7 +1173,7 @@ fn needless_borrow_impl_arg_position<'tcx>( } predicates.iter().all(|predicate| { - if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() + if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) && let ty::Param(param_ty) = trait_predicate.self_ty().kind() && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack() diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 1d9af7cdbd35..d870e0ceef47 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -14,7 +14,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, Binder, BoundConstness, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, + self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty, TyCtxt, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -499,7 +499,7 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> let ty_predicates = tcx.predicates_of(did).predicates; for (p, _) in ty_predicates { - if let PredicateKind::Trait(p) = p.kind().skip_binder() + if let PredicateKind::Clause(Clause::Trait(p)) = p.kind().skip_binder() && p.trait_ref.def_id == eq_trait_id && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() && p.constness == BoundConstness::NotConst @@ -512,14 +512,14 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv::new( tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain( params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { - tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate { + tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Trait(TraitPredicate { trait_ref: TraitRef::new( eq_trait_id, tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))), ), constness: BoundConstness::NotConst, polarity: ImplPolarity::Positive, - }))) + })))) }), )), Reveal::UserFacing, diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 0519f9ac2468..a9425a40f885 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -4,7 +4,7 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{EarlyBinder, Opaque, PredicateKind::Trait}; +use rustc_middle::ty::{Clause, EarlyBinder, Opaque, PredicateKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { infcx .err_ctxt() .maybe_note_obligation_cause_for_async_await(db, &obligation); - if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { + if let PredicateKind::Clause(Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() { db.note(&format!( "`{}` doesn't implement `{}`", trait_pred.self_ty(), diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 8b000cd754cd..7ff13b95626b 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -17,7 +17,7 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::EarlyBinder; -use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; +use rustc_middle::ty::{self, Clause, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; use rustc_semver::RustcVersion; use rustc_span::{sym, Symbol}; use rustc_trait_selection::traits::{ @@ -341,12 +341,12 @@ fn get_input_traits_and_projections<'tcx>( let mut projection_predicates = Vec::new(); for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() { match predicate.kind().skip_binder() { - PredicateKind::Trait(trait_predicate) => { + PredicateKind::Clause(Clause::Trait(trait_predicate)) => { if trait_predicate.trait_ref.self_ty() == input { trait_predicates.push(trait_predicate); } } - PredicateKind::Projection(projection_predicate) => { + PredicateKind::Clause(Clause::Projection(projection_predicate)) => { if projection_predicate.projection_ty.self_ty() == input { projection_predicates.push(projection_predicate); } @@ -403,7 +403,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< let mut trait_predicates = cx.tcx.param_env(callee_def_id) .caller_bounds().iter().filter(|predicate| { - if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() + if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() && trait_predicate.trait_ref.self_ty() == *param_ty { true } else { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 55599cd03ebe..75e12715458f 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { .filter_map(|obligation| { // Note that we do not want to deal with qualified predicates here. match obligation.predicate.kind().no_bound_vars() { - Some(ty::PredicateKind::Trait(pred)) if pred.def_id() != sized_trait => Some(pred), + Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => Some(pred), _ => None, } }) diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 8c4cff66f554..d28e97b79435 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -19,7 +19,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, Binder, ExistentialPredicate, List, PredicateKind, Ty}; +use rustc_middle::ty::{self, Binder, Clause, ExistentialPredicate, List, PredicateKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; @@ -699,7 +699,7 @@ fn matches_preds<'tcx>( ObligationCause::dummy(), cx.param_env, cx.tcx.mk_predicate(Binder::dummy( - PredicateKind::Projection(p.with_self_ty(cx.tcx, ty)), + PredicateKind::Clause(Clause::Projection(p.with_self_ty(cx.tcx, ty))), )), )), ExistentialPredicate::AutoTrait(p) => infcx diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 1307288623f9..a138a4baa9b3 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -4,7 +4,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; +use rustc_middle::ty::{Clause, GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, BytePos, Span}; @@ -45,7 +45,7 @@ fn get_trait_predicates_for_trait_id<'tcx>( let mut preds = Vec::new(); for (pred, _) in generics.predicates { if_chain! { - if let PredicateKind::Trait(poly_trait_pred) = pred.kind().skip_binder(); + if let PredicateKind::Clause(Clause::Trait(poly_trait_pred)) = pred.kind().skip_binder(); let trait_pred = cx.tcx.erase_late_bound_regions(pred.kind().rebind(poly_trait_pred)); if let Some(trait_def_id) = trait_id; if trait_def_id == trait_pred.trait_ref.def_id; @@ -63,7 +63,7 @@ fn get_projection_pred<'tcx>( trait_pred: TraitPredicate<'tcx>, ) -> Option> { generics.predicates.iter().find_map(|(proj_pred, _)| { - if let ty::PredicateKind::Projection(pred) = proj_pred.kind().skip_binder() { + if let ty::PredicateKind::Clause(Clause::Projection(pred)) = proj_pred.kind().skip_binder() { let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred.kind().rebind(pred)); if projection_pred.projection_ty.substs == trait_pred.trait_ref.substs { return Some(projection_pred); diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 95b3e651e2b5..f74f7dadfa90 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -73,7 +73,7 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: .flat_map(|v| v.fields.iter()) .any(|x| matches!(cx.tcx.type_of(x.did).peel_refs().kind(), ty::Param(_))) && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { - PredicateKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, + PredicateKind::Clause(ty::Clause::Trait(pred)) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, _ => true, }) && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_))) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index d183e28f667c..b8c2dd5ab9ea 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -25,13 +25,13 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option< let predicates = tcx.predicates_of(current); for (predicate, _) in predicates.predicates { match predicate.kind().skip_binder() { - ty::PredicateKind::RegionOutlives(_) - | ty::PredicateKind::TypeOutlives(_) + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_)) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_)) | ty::PredicateKind::WellFormed(_) - | ty::PredicateKind::Projection(_) + | ty::PredicateKind::Clause(ty::Clause::Projection(_)) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::Trait(..) + | ty::PredicateKind::Clause(ty::Clause::Trait(..)) | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"), diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 8284dc5c28c0..f4459e3e6633 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -81,7 +81,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through // and check substituions to find `U`. - ty::PredicateKind::Trait(trait_predicate) => { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { if trait_predicate .trait_ref .substs @@ -94,7 +94,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' }, // For `impl Trait`, it will register a predicate of `::Assoc = U`, // so we check the term for `U`. - ty::PredicateKind::Projection(projection_predicate) => { + ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => { if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() { if contains_ty_adt_constructor_opaque(cx, ty, needle) { return true; @@ -239,7 +239,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)), ty::Opaque(def_id, _) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { - if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { return true; } @@ -658,7 +658,7 @@ fn sig_from_bounds<'tcx>( for pred in predicates { match pred.kind().skip_binder() { - PredicateKind::Trait(p) + PredicateKind::Clause(ty::Clause::Trait(p)) if (lang_items.fn_trait() == Some(p.def_id()) || lang_items.fn_mut_trait() == Some(p.def_id()) || lang_items.fn_once_trait() == Some(p.def_id())) @@ -671,7 +671,7 @@ fn sig_from_bounds<'tcx>( } inputs = Some(i); }, - PredicateKind::Projection(p) + PredicateKind::Clause(ty::Clause::Projection(p)) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() && p.projection_ty.self_ty() == ty => { @@ -699,7 +699,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O .subst_iter_copied(cx.tcx, ty.substs) { match pred.kind().skip_binder() { - PredicateKind::Trait(p) + PredicateKind::Clause(ty::Clause::Trait(p)) if (lang_items.fn_trait() == Some(p.def_id()) || lang_items.fn_mut_trait() == Some(p.def_id()) || lang_items.fn_once_trait() == Some(p.def_id())) => @@ -712,7 +712,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O } inputs = Some(i); }, - PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => { + PredicateKind::Clause(ty::Clause::Projection(p)) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? return None; @@ -887,7 +887,7 @@ pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tc predicates .iter() .try_fold(false, |found, p| { - if let PredicateKind::Trait(p) = p.kind().skip_binder() + if let PredicateKind::Clause(ty::Clause::Trait(p)) = p.kind().skip_binder() && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() && ty.index == self_ty.index { From 424ae2395864051743d0fdd7bffafd0a75b5ac0f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 25 Nov 2022 08:47:59 +0100 Subject: [PATCH 253/524] RefCell::get_mut: fix typo and fix the same typo in a bunch of other places --- clippy_dev/src/setup/git_hook.rs | 2 +- clippy_utils/src/hir_utils.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_dev/src/setup/git_hook.rs b/clippy_dev/src/setup/git_hook.rs index 1de5b1940bae..c7c53bc69d0b 100644 --- a/clippy_dev/src/setup/git_hook.rs +++ b/clippy_dev/src/setup/git_hook.rs @@ -6,7 +6,7 @@ use super::verify_inside_clippy_dir; /// Rusts setup uses `git rev-parse --git-common-dir` to get the root directory of the repo. /// I've decided against this for the sake of simplicity and to make sure that it doesn't install /// the hook if `clippy_dev` would be used in the rust tree. The hook also references this tool -/// for formatting and should therefor only be used in a normal clone of clippy +/// for formatting and should therefore only be used in a normal clone of clippy const REPO_GIT_DIR: &str = ".git"; const HOOK_SOURCE_FILE: &str = "util/etc/pre-commit.sh"; const HOOK_TARGET_FILE: &str = ".git/hooks/pre-commit"; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index cf24ec8b67b9..abe10f3c81e5 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -113,7 +113,7 @@ impl HirEqInterExpr<'_, '_, '_> { } } - // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that + // eq_pat adds the HirIds to the locals map. We therefore call it last to make sure that // these only get added if the init and type is equal. both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) From a116b9bdba6c3c00f4544a937af690bd462ca428 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 14 Nov 2022 13:42:47 +0100 Subject: [PATCH 254/524] Lint unnecessary safety comments on items --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/undocumented_unsafe_blocks.rs | 240 +++++++++++++----- tests/ui/undocumented_unsafe_blocks.rs | 2 +- tests/ui/undocumented_unsafe_blocks.stderr | 15 +- 5 files changed, 200 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b6b12c623af..23912bb3ed6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4451,6 +4451,7 @@ Released 2018-09-13 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings +[`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment [`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index eb3210946f11..e4d76f07d6b4 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -584,6 +584,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::types::TYPE_COMPLEXITY_INFO, crate::types::VEC_BOX_INFO, crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO, + crate::undocumented_unsafe_blocks::UNNECESSARY_SAFETY_COMMENT_INFO, crate::unicode::INVISIBLE_CHARACTERS_INFO, crate::unicode::NON_ASCII_LITERAL_INFO, crate::unicode::UNICODE_NOT_NFC_INFO, diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index e8f15a444735..57ee2735aa03 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -59,8 +59,36 @@ declare_clippy_lint! { restriction, "creating an unsafe block without explaining why it is safe" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `// SAFETY: ` comments on safe code. + /// + /// ### Why is this bad? + /// Safe code has no safety requirements, so there is no need to + /// describe safety invariants. + /// + /// ### Example + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// // SAFETY: references are guaranteed to be non-null. + /// let ptr = NonNull::new(a).unwrap(); + /// ``` + /// Use instead: + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// let ptr = NonNull::new(a).unwrap(); + /// ``` + #[clippy::version = "1.66.0"] + pub UNNECESSARY_SAFETY_COMMENT, + restriction, + "creating an unsafe block without explaining why it is safe" +} -declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); +declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]); impl LateLintPass<'_> for UndocumentedUnsafeBlocks { fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) { @@ -90,28 +118,95 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { } fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if let hir::ItemKind::Impl(imple) = item.kind - && imple.unsafety == hir::Unsafety::Unsafe - && !in_external_macro(cx.tcx.sess, item.span) - && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) - && !is_unsafe_from_proc_macro(cx, item.span) - && !item_has_safety_comment(cx, item) - { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } + + let mk_spans = |pos: BytePos| { let source_map = cx.tcx.sess.source_map(); + let span = Span::new(pos, pos, SyntaxContext::root(), None); + let help_span = source_map.span_extend_to_next_char(span, '\n', true); let span = if source_map.is_multiline(item.span) { source_map.span_until_char(item.span, '\n') } else { item.span }; + (span, help_span) + }; - span_lint_and_help( - cx, - UNDOCUMENTED_UNSAFE_BLOCKS, - span, - "unsafe impl missing a safety comment", - None, - "consider adding a safety comment on the preceding line", - ); + let item_has_safety_comment = item_has_safety_comment(cx, item); + match (&item.kind, item_has_safety_comment) { + (hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => { + if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) + && !is_unsafe_from_proc_macro(cx, item.span) + { + let source_map = cx.tcx.sess.source_map(); + let span = if source_map.is_multiline(item.span) { + source_map.span_until_char(item.span, '\n') + } else { + item.span + }; + + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe impl missing a safety comment", + None, + "consider adding a safety comment on the preceding line", + ); + } + }, + (hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + "impl has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } + }, + (hir::ItemKind::Impl(_), _) => {}, + (&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) { + let body = cx.tcx.hir().body(body); + if !matches!( + body.value.kind, hir::ExprKind::Block(block, _) + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + ) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + &format!("{} has unnecessary safety comment", item.kind.descr()), + Some(help_span), + "consider removing the safety comment", + ); + } + } + }, + (_, HasSafetyComment::Yes(pos)) => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + &format!("{} has unnecessary safety comment", item.kind.descr()), + Some(help_span), + "consider removing the safety comment", + ); + } + }, + _ => (), } } } @@ -170,28 +265,38 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // won't work. This is to avoid dealing with where such a comment should be place relative to // attributes and doc comments. - span_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span) + matches!( + span_from_macro_expansion_has_safety_comment(cx, span), + HasSafetyComment::Yes(_) + ) || span_in_body_has_safety_comment(cx, span) +} + +enum HasSafetyComment { + Yes(BytePos), + No, + Maybe, } /// Checks if the lines immediately preceding the item contain a safety comment. #[allow(clippy::collapsible_match)] -fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { - if span_from_macro_expansion_has_safety_comment(cx, item.span) { - return true; +fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, item.span) { + HasSafetyComment::Maybe => (), + has_safety_comment => return has_safety_comment, } if item.span.ctxt() == SyntaxContext::root() { if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { let comment_start = match parent_node { Node::Crate(parent_mod) => { - comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) + comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) }, Node::Item(parent_item) => { if let ItemKind::Mod(parent_mod) = &parent_item.kind { - comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item) + comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item) } else { // Doesn't support impls in this position. Pretend a comment was found. - return true; + return HasSafetyComment::Maybe; } }, Node::Stmt(stmt) => { @@ -200,17 +305,17 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), _ => { // Doesn't support impls in this position. Pretend a comment was found. - return true; + return HasSafetyComment::Maybe; }, } } else { // Problem getting the parent node. Pretend a comment was found. - return true; + return HasSafetyComment::Maybe; } }, _ => { // Doesn't support impls in this position. Pretend a comment was found. - return true; + return HasSafetyComment::Maybe; }, }; @@ -222,33 +327,40 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { && let Some(src) = unsafe_line.sf.src.as_deref() { unsafe_line.sf.lines(|lines| { - comment_start_line.line < unsafe_line.line && text_has_safety_comment( - src, - &lines[comment_start_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &lines[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } }) } else { // Problem getting source text. Pretend a comment was found. - true + HasSafetyComment::Maybe } } else { // No parent node. Pretend a comment was found. - true + HasSafetyComment::Maybe } } else { - false + HasSafetyComment::No } } -fn comment_start_before_impl_in_mod( +fn comment_start_before_item_in_mod( cx: &LateContext<'_>, parent_mod: &hir::Mod<'_>, parent_mod_span: Span, - imple: &hir::Item<'_>, + item: &hir::Item<'_>, ) -> Option { parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| { - if *item_id == imple.item_id() { + if *item_id == item.item_id() { if idx == 0 { // mod A { /* comment */ unsafe impl T {} ... } // ^------------------------------------------^ returns the start of this span @@ -270,11 +382,11 @@ fn comment_start_before_impl_in_mod( }) } -fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); if ctxt == SyntaxContext::root() { - false + HasSafetyComment::Maybe } else { // From a macro expansion. Get the text from the start of the macro declaration to start of the // unsafe block. @@ -286,15 +398,22 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && let Some(src) = unsafe_line.sf.src.as_deref() { unsafe_line.sf.lines(|lines| { - macro_line.line < unsafe_line.line && text_has_safety_comment( - src, - &lines[macro_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + if macro_line.line < unsafe_line.line { + match text_has_safety_comment( + src, + &lines[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } else { + HasSafetyComment::No + } }) } else { // Problem getting source text. Pretend a comment was found. - true + HasSafetyComment::Maybe } } } @@ -333,7 +452,7 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { src, &lines[body_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos.to_usize(), - ) + ).is_some() }) } else { // Problem getting source text. Pretend a comment was found. @@ -345,30 +464,34 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { } /// Checks if the given text has a safety comment for the immediately proceeding line. -fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool { +fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> Option { let mut lines = line_starts .array_windows::<2>() .rev() .map_while(|[start, end]| { let start = start.to_usize() - offset; let end = end.to_usize() - offset; - src.get(start..end).map(|text| (start, text.trim_start())) + let text = src.get(start..end)?; + let trimmed = text.trim_start(); + Some((start + (text.len() - trimmed.len()), trimmed)) }) .filter(|(_, text)| !text.is_empty()); let Some((line_start, line)) = lines.next() else { - return false; + return None; }; // Check for a sequence of line comments. if line.starts_with("//") { - let mut line = line; + let (mut line, mut line_start) = (line, line_start); loop { if line.to_ascii_uppercase().contains("SAFETY:") { - return true; + return Some(BytePos( + u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(), + )); } match lines.next() { - Some((_, x)) if x.starts_with("//") => line = x, - _ => return false, + Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s), + _ => return None, } } } @@ -377,16 +500,19 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> let (mut line_start, mut line) = (line_start, line); loop { if line.starts_with("/*") { - let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start(); + let src = &src[line_start..line_starts.last().unwrap().to_usize() - offset]; let mut tokens = tokenize(src); - return src[..tokens.next().unwrap().len as usize] + return (src[..tokens.next().unwrap().len as usize] .to_ascii_uppercase() .contains("SAFETY:") - && tokens.all(|t| t.kind == TokenKind::Whitespace); + && tokens.all(|t| t.kind == TokenKind::Whitespace)) + .then_some(BytePos( + u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(), + )); } match lines.next() { Some(x) => (line_start, line) = x, - None => return false, + None => return None, } } } diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index cbc6768033ec..c05eb447b2eb 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,6 +1,6 @@ // aux-build:proc_macro_unsafe.rs -#![warn(clippy::undocumented_unsafe_blocks)] +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] #![allow(clippy::let_unit_value, clippy::missing_safety_doc)] extern crate proc_macro_unsafe; diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index ba4de9806d17..4c6f6cd18c51 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -239,6 +239,19 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: | = help: consider adding a safety comment on the preceding line +error: constant item has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:471:5 + | +LL | const BIG_NUMBER: i32 = 1000000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:470:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + error: unsafe impl missing a safety comment --> $DIR/undocumented_unsafe_blocks.rs:472:5 | @@ -287,5 +300,5 @@ LL | let bar = unsafe {}; | = help: consider adding a safety comment on the preceding line -error: aborting due to 34 previous errors +error: aborting due to 35 previous errors From b8c3f64cee8b07470572f274712dc9ab2634221b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 17 Nov 2022 11:00:51 +0100 Subject: [PATCH 255/524] Add some more test cases for undocumented_unsafe_blocks --- .../src/undocumented_unsafe_blocks.rs | 7 +- tests/ui/undocumented_unsafe_blocks.rs | 13 ++++ tests/ui/undocumented_unsafe_blocks.stderr | 72 +++++++++++++++++-- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 57ee2735aa03..080a481e87f5 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -82,7 +82,7 @@ declare_clippy_lint! { /// /// let ptr = NonNull::new(a).unwrap(); /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.67.0"] pub UNNECESSARY_SAFETY_COMMENT, restriction, "creating an unsafe block without explaining why it is safe" @@ -136,6 +136,7 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { let item_has_safety_comment = item_has_safety_comment(cx, item); match (&item.kind, item_has_safety_comment) { + // lint unsafe impl without safety comment (hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => { if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) && !is_unsafe_from_proc_macro(cx, item.span) @@ -157,6 +158,7 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { ); } }, + // lint safe impl with unnecessary safety comment (hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { let (span, help_span) = mk_spans(pos); @@ -172,6 +174,7 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { } }, (hir::ItemKind::Impl(_), _) => {}, + // const and static items only need a safety comment if their body is an unsafe block, lint otherwise (&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) { let body = cx.tcx.hir().body(body); @@ -192,6 +195,8 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { } } }, + // Aside from unsafe impls and consts/statics with an unsafe block, items in general + // do not have safety invariants that need to be documented, so lint those. (_, HasSafetyComment::Yes(pos)) => { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { let (span, help_span) = mk_spans(pos); diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index c05eb447b2eb..f68e0b4915ea 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -472,6 +472,19 @@ mod unsafe_impl_invalid_comment { unsafe impl Interference for () {} } +mod unsafe_items_invalid_comment { + // SAFETY: + const CONST: u32 = 0; + // SAFETY: + static STATIC: u32 = 0; + // SAFETY: + struct Struct; + // SAFETY: + enum Enum {} + // SAFETY: + mod module {} +} + unsafe trait ImplInFn {} fn impl_in_fn() { diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 4c6f6cd18c51..becad4f61a92 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -260,16 +260,76 @@ LL | unsafe impl Interference for () {} | = help: consider adding a safety comment on the preceding line -error: unsafe impl missing a safety comment +error: constant item has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:477:5 + | +LL | const CONST: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:476:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: static item has unnecessary safety comment --> $DIR/undocumented_unsafe_blocks.rs:479:5 | +LL | static STATIC: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:478:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: struct has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:481:5 + | +LL | struct Struct; + | ^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:480:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: enum has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:483:5 + | +LL | enum Enum {} + | ^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:482:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: module has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:485:5 + | +LL | mod module {} + | ^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:484:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:492:5 + | LL | unsafe impl ImplInFn for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:488:1 + --> $DIR/undocumented_unsafe_blocks.rs:501:1 | LL | unsafe impl CrateRoot for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -277,7 +337,7 @@ LL | unsafe impl CrateRoot for () {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:498:9 + --> $DIR/undocumented_unsafe_blocks.rs:511:9 | LL | unsafe {}; | ^^^^^^^^^ @@ -285,7 +345,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:502:12 + --> $DIR/undocumented_unsafe_blocks.rs:515:12 | LL | if unsafe { true } { | ^^^^^^^^^^^^^^^ @@ -293,12 +353,12 @@ LL | if unsafe { true } { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:505:23 + --> $DIR/undocumented_unsafe_blocks.rs:518:23 | LL | let bar = unsafe {}; | ^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: aborting due to 35 previous errors +error: aborting due to 40 previous errors From 4fa57575302a5931517590daf8df2ef8a93f7a7b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 17 Nov 2022 15:14:03 +0100 Subject: [PATCH 256/524] Lint unnecessary safety comments on statements and block tail expressions --- .../src/undocumented_unsafe_blocks.rs | 126 +++++++++++++++++- clippy_utils/src/visitors.rs | 12 +- tests/ui/undocumented_unsafe_blocks.rs | 31 +++++ tests/ui/undocumented_unsafe_blocks.stderr | 60 ++++++++- 4 files changed, 220 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 080a481e87f5..c60144df757c 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,6 +1,10 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::walk_span_to_context; +use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{get_parent_node, is_lint_allowed}; +use hir::HirId; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; @@ -90,8 +94,8 @@ declare_clippy_lint! { declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]); -impl LateLintPass<'_> for UndocumentedUnsafeBlocks { - fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) { +impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) && !in_external_macro(cx.tcx.sess, block.span) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) @@ -115,6 +119,45 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { "consider adding a safety comment on the preceding line", ); } + + if let Some(tail) = block.expr + && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) + && !in_external_macro(cx.tcx.sess, tail.span) + && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id) + && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) + { + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + tail.span, + "expression has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) { + let expr = match stmt.kind { + hir::StmtKind::Local(&hir::Local { init: Some(expr), .. }) + | hir::StmtKind::Expr(expr) + | hir::StmtKind::Semi(expr) => expr, + _ => return, + }; + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) + && !in_external_macro(cx.tcx.sess, stmt.span) + && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) + && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) + { + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + stmt.span, + "statement has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } } fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { @@ -216,6 +259,36 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { } } +fn expr_has_unnecessary_safety_comment<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + comment_pos: BytePos, +) -> Option { + // this should roughly be the reverse of `block_parents_have_safety_comment` + if for_each_expr_with_closures(cx, expr, |expr| match expr.kind { + hir::ExprKind::Block( + Block { + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }, + _, + ) => ControlFlow::Break(()), + // statements will be handled by check_stmt itself again + hir::ExprKind::Block(..) => ControlFlow::Continue(Descend::No), + _ => ControlFlow::Continue(Descend::Yes), + }) + .is_some() + { + return None; + } + + let source_map = cx.tcx.sess.source_map(); + let span = Span::new(comment_pos, comment_pos, SyntaxContext::root(), None); + let help_span = source_map.span_extend_to_next_char(span, '\n', true); + + Some(help_span) +} + fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); let file_pos = source_map.lookup_byte_offset(span.lo()); @@ -358,6 +431,55 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf } } +/// Checks if the lines immediately preceding the item contain a safety comment. +#[allow(clippy::collapsible_match)] +fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, span) { + HasSafetyComment::Maybe => (), + has_safety_comment => return has_safety_comment, + } + + if span.ctxt() == SyntaxContext::root() { + if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) { + let comment_start = match parent_node { + Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), + _ => return HasSafetyComment::Maybe, + }; + + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + unsafe_line.sf.lines(|lines| { + if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &lines[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } + }) + } else { + // Problem getting source text. Pretend a comment was found. + HasSafetyComment::Maybe + } + } else { + // No parent node. Pretend a comment was found. + HasSafetyComment::Maybe + } + } else { + HasSafetyComment::No + } +} + fn comment_start_before_item_in_mod( cx: &LateContext<'_>, parent_mod: &hir::Mod<'_>, diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index d4294f18fd50..863fb60fcfca 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -170,22 +170,22 @@ where cb: F, } - struct WithStmtGuarg<'a, F> { + struct WithStmtGuard<'a, F> { val: &'a mut RetFinder, prev_in_stmt: bool, } impl RetFinder { - fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { + fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> { let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); - WithStmtGuarg { + WithStmtGuard { val: self, prev_in_stmt, } } } - impl std::ops::Deref for WithStmtGuarg<'_, F> { + impl std::ops::Deref for WithStmtGuard<'_, F> { type Target = RetFinder; fn deref(&self) -> &Self::Target { @@ -193,13 +193,13 @@ where } } - impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + impl std::ops::DerefMut for WithStmtGuard<'_, F> { fn deref_mut(&mut self) -> &mut Self::Target { self.val } } - impl Drop for WithStmtGuarg<'_, F> { + impl Drop for WithStmtGuard<'_, F> { fn drop(&mut self) { self.val.in_stmt = self.prev_in_stmt; } diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index f68e0b4915ea..cb99ce0d4214 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -522,4 +522,35 @@ fn issue_9142() { }; } +mod unnecessary_from_macro { + trait T {} + + macro_rules! no_safety_comment { + ($t:ty) => { + impl T for $t {} + }; + } + + // FIXME: This is not caught + // Safety: unnecessary + no_safety_comment!(()); + + macro_rules! with_safety_comment { + ($t:ty) => { + // Safety: unnecessary + impl T for $t {} + }; + } + + with_safety_comment!(i32); +} + +fn unnecessary_on_stmt_and_expr() -> u32 { + // SAFETY: unnecessary + let num = 42; + + // SAFETY: unnecessary + 24 +} + fn main() {} diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index becad4f61a92..919fd51351cb 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -344,6 +344,24 @@ LL | unsafe {}; | = help: consider adding a safety comment on the preceding line +error: statement has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:514:5 + | +LL | / let _ = { +LL | | if unsafe { true } { +LL | | todo!(); +LL | | } else { +... | +LL | | } +LL | | }; + | |______^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:513:5 + | +LL | // SAFETY: this is more than one level away, so it should warn + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: unsafe block missing a safety comment --> $DIR/undocumented_unsafe_blocks.rs:515:12 | @@ -360,5 +378,45 @@ LL | let bar = unsafe {}; | = help: consider adding a safety comment on the preceding line -error: aborting due to 40 previous errors +error: impl has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:541:13 + | +LL | impl T for $t {} + | ^^^^^^^^^^^^^^^^ +... +LL | with_safety_comment!(i32); + | ------------------------- in this macro invocation + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:540:13 + | +LL | // Safety: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expression has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:553:5 + | +LL | 24 + | ^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:552:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: statement has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:550:5 + | +LL | let num = 42; + | ^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:549:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 44 previous errors From 9c69e1cc893878987991d77b8fb54d1f6de29733 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 17 Nov 2022 15:17:28 +0100 Subject: [PATCH 257/524] Simplify --- .../src/undocumented_unsafe_blocks.rs | 173 ++++++++---------- 1 file changed, 78 insertions(+), 95 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index c60144df757c..d7e483423064 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -363,72 +363,60 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf has_safety_comment => return has_safety_comment, } - if item.span.ctxt() == SyntaxContext::root() { - if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { - let comment_start = match parent_node { - Node::Crate(parent_mod) => { - comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) - }, - Node::Item(parent_item) => { - if let ItemKind::Mod(parent_mod) = &parent_item.kind { - comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item) - } else { - // Doesn't support impls in this position. Pretend a comment was found. - return HasSafetyComment::Maybe; - } - }, - Node::Stmt(stmt) => { - if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) { - match stmt_parent { - Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), - _ => { - // Doesn't support impls in this position. Pretend a comment was found. - return HasSafetyComment::Maybe; - }, - } - } else { - // Problem getting the parent node. Pretend a comment was found. - return HasSafetyComment::Maybe; - } - }, - _ => { + if item.span.ctxt() != SyntaxContext::root() { + return HasSafetyComment::No; + } + if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { + let comment_start = match parent_node { + Node::Crate(parent_mod) => { + comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) + }, + Node::Item(parent_item) => { + if let ItemKind::Mod(parent_mod) = &parent_item.kind { + comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item) + } else { // Doesn't support impls in this position. Pretend a comment was found. return HasSafetyComment::Maybe; - }, - }; + } + }, + Node::Stmt(stmt) => { + if let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) { + walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo) + } else { + // Problem getting the parent node. Pretend a comment was found. + return HasSafetyComment::Maybe; + } + }, + _ => { + // Doesn't support impls in this position. Pretend a comment was found. + return HasSafetyComment::Maybe; + }, + }; - let source_map = cx.sess().source_map(); - if let Some(comment_start) = comment_start - && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) - && let Ok(comment_start_line) = source_map.lookup_line(comment_start) - && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) - && let Some(src) = unsafe_line.sf.src.as_deref() - { - unsafe_line.sf.lines(|lines| { - if comment_start_line.line >= unsafe_line.line { - HasSafetyComment::No - } else { - match text_has_safety_comment( - src, - &lines[comment_start_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + return unsafe_line.sf.lines(|lines| { + if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &lines[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }) - } else { - // Problem getting source text. Pretend a comment was found. - HasSafetyComment::Maybe - } - } else { - // No parent node. Pretend a comment was found. - HasSafetyComment::Maybe + } + }); } - } else { - HasSafetyComment::No } + HasSafetyComment::Maybe } /// Checks if the lines immediately preceding the item contain a safety comment. @@ -439,45 +427,40 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H has_safety_comment => return has_safety_comment, } - if span.ctxt() == SyntaxContext::root() { - if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) { - let comment_start = match parent_node { - Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), - _ => return HasSafetyComment::Maybe, - }; + if span.ctxt() != SyntaxContext::root() { + return HasSafetyComment::No; + } - let source_map = cx.sess().source_map(); - if let Some(comment_start) = comment_start - && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) - && let Ok(comment_start_line) = source_map.lookup_line(comment_start) - && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) - && let Some(src) = unsafe_line.sf.src.as_deref() - { - unsafe_line.sf.lines(|lines| { - if comment_start_line.line >= unsafe_line.line { - HasSafetyComment::No - } else { - match text_has_safety_comment( - src, - &lines[comment_start_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) { - Some(b) => HasSafetyComment::Yes(b), - None => HasSafetyComment::No, - } + if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) { + let comment_start = match parent_node { + Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), + _ => return HasSafetyComment::Maybe, + }; + + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + return unsafe_line.sf.lines(|lines| { + if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &lines[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, } - }) - } else { - // Problem getting source text. Pretend a comment was found. - HasSafetyComment::Maybe - } - } else { - // No parent node. Pretend a comment was found. - HasSafetyComment::Maybe + } + }); } - } else { - HasSafetyComment::No } + HasSafetyComment::Maybe } fn comment_start_before_item_in_mod( From f96dd383188e9f24e5b401e664cca97b8bd73825 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 24 Nov 2022 09:47:50 +0100 Subject: [PATCH 258/524] Address reviews --- .../src/undocumented_unsafe_blocks.rs | 9 +- tests/ui/undocumented_unsafe_blocks.rs | 44 ------- tests/ui/undocumented_unsafe_blocks.stderr | 116 ++---------------- tests/ui/unnecessary_safety_comment.rs | 51 ++++++++ tests/ui/unnecessary_safety_comment.stderr | 115 +++++++++++++++++ 5 files changed, 178 insertions(+), 157 deletions(-) create mode 100644 tests/ui/unnecessary_safety_comment.rs create mode 100644 tests/ui/unnecessary_safety_comment.stderr diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index d7e483423064..2e1b6d8d4ea7 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -89,7 +89,7 @@ declare_clippy_lint! { #[clippy::version = "1.67.0"] pub UNNECESSARY_SAFETY_COMMENT, restriction, - "creating an unsafe block without explaining why it is safe" + "annotating safe code with a safety comment" } declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]); @@ -138,12 +138,11 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) { - let expr = match stmt.kind { + let ( hir::StmtKind::Local(&hir::Local { init: Some(expr), .. }) | hir::StmtKind::Expr(expr) - | hir::StmtKind::Semi(expr) => expr, - _ => return, - }; + | hir::StmtKind::Semi(expr) + ) = stmt.kind else { return }; if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) && !in_external_macro(cx.tcx.sess, stmt.span) && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index cb99ce0d4214..c05eb447b2eb 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -472,19 +472,6 @@ mod unsafe_impl_invalid_comment { unsafe impl Interference for () {} } -mod unsafe_items_invalid_comment { - // SAFETY: - const CONST: u32 = 0; - // SAFETY: - static STATIC: u32 = 0; - // SAFETY: - struct Struct; - // SAFETY: - enum Enum {} - // SAFETY: - mod module {} -} - unsafe trait ImplInFn {} fn impl_in_fn() { @@ -522,35 +509,4 @@ fn issue_9142() { }; } -mod unnecessary_from_macro { - trait T {} - - macro_rules! no_safety_comment { - ($t:ty) => { - impl T for $t {} - }; - } - - // FIXME: This is not caught - // Safety: unnecessary - no_safety_comment!(()); - - macro_rules! with_safety_comment { - ($t:ty) => { - // Safety: unnecessary - impl T for $t {} - }; - } - - with_safety_comment!(i32); -} - -fn unnecessary_on_stmt_and_expr() -> u32 { - // SAFETY: unnecessary - let num = 42; - - // SAFETY: unnecessary - 24 -} - fn main() {} diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 919fd51351cb..d1c1bb5ffeac 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -260,68 +260,8 @@ LL | unsafe impl Interference for () {} | = help: consider adding a safety comment on the preceding line -error: constant item has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:477:5 - | -LL | const CONST: u32 = 0; - | ^^^^^^^^^^^^^^^^^^^^^ - | -help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:476:5 - | -LL | // SAFETY: - | ^^^^^^^^^^ - -error: static item has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:479:5 - | -LL | static STATIC: u32 = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:478:5 - | -LL | // SAFETY: - | ^^^^^^^^^^ - -error: struct has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:481:5 - | -LL | struct Struct; - | ^^^^^^^^^^^^^^ - | -help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:480:5 - | -LL | // SAFETY: - | ^^^^^^^^^^ - -error: enum has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:483:5 - | -LL | enum Enum {} - | ^^^^^^^^^^^^ - | -help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:482:5 - | -LL | // SAFETY: - | ^^^^^^^^^^ - -error: module has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:485:5 - | -LL | mod module {} - | ^^^^^^^^^^^^^ - | -help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:484:5 - | -LL | // SAFETY: - | ^^^^^^^^^^ - error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:492:5 + --> $DIR/undocumented_unsafe_blocks.rs:479:5 | LL | unsafe impl ImplInFn for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -329,7 +269,7 @@ LL | unsafe impl ImplInFn for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:501:1 + --> $DIR/undocumented_unsafe_blocks.rs:488:1 | LL | unsafe impl CrateRoot for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -337,7 +277,7 @@ LL | unsafe impl CrateRoot for () {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:511:9 + --> $DIR/undocumented_unsafe_blocks.rs:498:9 | LL | unsafe {}; | ^^^^^^^^^ @@ -345,7 +285,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: statement has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:514:5 + --> $DIR/undocumented_unsafe_blocks.rs:501:5 | LL | / let _ = { LL | | if unsafe { true } { @@ -357,13 +297,13 @@ LL | | }; | |______^ | help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:513:5 + --> $DIR/undocumented_unsafe_blocks.rs:500:5 | LL | // SAFETY: this is more than one level away, so it should warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:515:12 + --> $DIR/undocumented_unsafe_blocks.rs:502:12 | LL | if unsafe { true } { | ^^^^^^^^^^^^^^^ @@ -371,52 +311,12 @@ LL | if unsafe { true } { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:518:23 + --> $DIR/undocumented_unsafe_blocks.rs:505:23 | LL | let bar = unsafe {}; | ^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: impl has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:541:13 - | -LL | impl T for $t {} - | ^^^^^^^^^^^^^^^^ -... -LL | with_safety_comment!(i32); - | ------------------------- in this macro invocation - | -help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:540:13 - | -LL | // Safety: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: expression has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:553:5 - | -LL | 24 - | ^^ - | -help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:552:5 - | -LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: statement has unnecessary safety comment - --> $DIR/undocumented_unsafe_blocks.rs:550:5 - | -LL | let num = 42; - | ^^^^^^^^^^^^^ - | -help: consider removing the safety comment - --> $DIR/undocumented_unsafe_blocks.rs:549:5 - | -LL | // SAFETY: unnecessary - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 44 previous errors +error: aborting due to 36 previous errors diff --git a/tests/ui/unnecessary_safety_comment.rs b/tests/ui/unnecessary_safety_comment.rs new file mode 100644 index 000000000000..7fefea7051d6 --- /dev/null +++ b/tests/ui/unnecessary_safety_comment.rs @@ -0,0 +1,51 @@ +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] +#![allow(clippy::let_unit_value, clippy::missing_safety_doc)] + +mod unsafe_items_invalid_comment { + // SAFETY: + const CONST: u32 = 0; + // SAFETY: + static STATIC: u32 = 0; + // SAFETY: + struct Struct; + // SAFETY: + enum Enum {} + // SAFETY: + mod module {} +} + +mod unnecessary_from_macro { + trait T {} + + macro_rules! no_safety_comment { + ($t:ty) => { + impl T for $t {} + }; + } + + // FIXME: This is not caught + // Safety: unnecessary + no_safety_comment!(()); + + macro_rules! with_safety_comment { + ($t:ty) => { + // Safety: unnecessary + impl T for $t {} + }; + } + + with_safety_comment!(i32); +} + +fn unnecessary_on_stmt_and_expr() -> u32 { + // SAFETY: unnecessary + let num = 42; + + // SAFETY: unnecessary + if num > 24 {} + + // SAFETY: unnecessary + 24 +} + +fn main() {} diff --git a/tests/ui/unnecessary_safety_comment.stderr b/tests/ui/unnecessary_safety_comment.stderr new file mode 100644 index 000000000000..7b2af67d64c7 --- /dev/null +++ b/tests/ui/unnecessary_safety_comment.stderr @@ -0,0 +1,115 @@ +error: constant item has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:6:5 + | +LL | const CONST: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:5:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + +error: static item has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:8:5 + | +LL | static STATIC: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:7:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: struct has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:10:5 + | +LL | struct Struct; + | ^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:9:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: enum has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:12:5 + | +LL | enum Enum {} + | ^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:11:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: module has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:14:5 + | +LL | mod module {} + | ^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:13:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: impl has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:33:13 + | +LL | impl T for $t {} + | ^^^^^^^^^^^^^^^^ +... +LL | with_safety_comment!(i32); + | ------------------------- in this macro invocation + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:32:13 + | +LL | // Safety: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expression has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:48:5 + | +LL | 24 + | ^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:47:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: statement has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:42:5 + | +LL | let num = 42; + | ^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:41:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: statement has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:45:5 + | +LL | if num > 24 {} + | ^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:44:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + From bbcc260b6feecdeadd2b366cc8f021cc3404c9ec Mon Sep 17 00:00:00 2001 From: dswij Date: Fri, 25 Nov 2022 18:04:17 +0800 Subject: [PATCH 259/524] `manual_let_else`: keep macro call on suggestion blocks --- clippy_lints/src/manual_let_else.rs | 30 ++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 20f06830952c..91f0dc2a7170 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::peel_blocks; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{for_each_expr, Descend}; use if_chain::if_chain; @@ -143,18 +143,22 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: // for this to be machine applicable. let app = Applicability::HasPlaceholders; - if let Some(sn_pat) = snippet_opt(cx, pat.span) && - let Some(sn_expr) = snippet_opt(cx, expr.span) && - let Some(sn_else) = snippet_opt(cx, else_body.span) - { - let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { - sn_else - } else { - format!("{{ {sn_else} }}") - }; - let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};"); - diag.span_suggestion(span, "consider writing", sugg, app); - } + let snippet_fn = if span.from_expansion() { + snippet + } else { + snippet_with_macro_callsite + }; + let sn_pat = snippet_fn(cx, pat.span, ""); + let sn_expr = snippet_fn(cx, expr.span, ""); + let sn_else = snippet_fn(cx, else_body.span, ""); + + let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { + sn_else.into_owned() + } else { + format!("{{ {sn_else} }}") + }; + let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};"); + diag.span_suggestion(span, "consider writing", sugg, app); }, ); } From a4b53c9c14705475b6e9041b56c441c981e86423 Mon Sep 17 00:00:00 2001 From: dswij Date: Fri, 25 Nov 2022 18:04:31 +0800 Subject: [PATCH 260/524] `manual_let_else`: Add test with expanded macros --- tests/ui/manual_let_else.rs | 14 ++++++++++++++ tests/ui/manual_let_else.stderr | 11 ++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index 2ef40e5911af..48a162c13602 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -234,4 +234,18 @@ fn not_fire() { // If a type annotation is present, don't lint as // expressing the type might be too hard let v: () = if let Some(v_some) = g() { v_some } else { panic!() }; + + // Issue 9940 + // Suggestion should not expand macros + macro_rules! macro_call { + () => { + return () + }; + } + + let ff = Some(1); + let _ = match ff { + Some(value) => value, + _ => macro_call!(), + }; } diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index 453b68b8bd00..52aac6bc673d 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -259,5 +259,14 @@ LL | create_binding_if_some!(w, g()); | = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 17 previous errors +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:247:5 + | +LL | / let _ = match ff { +LL | | Some(value) => value, +LL | | _ => macro_call!(), +LL | | }; + | |______^ help: consider writing: `let Some(value) = ff else { macro_call!() };` + +error: aborting due to 18 previous errors From 4faf11a102756ff81e4bf7105650c4429397911b Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 25 Nov 2022 11:39:04 +0100 Subject: [PATCH 261/524] Move syntax tree patterns RFC to the book --- book/src/SUMMARY.md | 1 + .../src/development/proposals/syntax-tree-patterns.md | 0 2 files changed, 1 insertion(+) rename rfcs/0001-syntax-tree-patterns.md => book/src/development/proposals/syntax-tree-patterns.md (100%) diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 0b945faf9b78..1f0b8db28a15 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -21,3 +21,4 @@ - [The Clippy Book](development/infrastructure/book.md) - [Proposals](development/proposals/README.md) - [Roadmap 2021](development/proposals/roadmap-2021.md) + - [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md) diff --git a/rfcs/0001-syntax-tree-patterns.md b/book/src/development/proposals/syntax-tree-patterns.md similarity index 100% rename from rfcs/0001-syntax-tree-patterns.md rename to book/src/development/proposals/syntax-tree-patterns.md From c6a1184e4d4ca23fcbc6656913ba5a5a21d54368 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 25 Nov 2022 11:39:36 +0100 Subject: [PATCH 262/524] Book: Format syntax tree pattern proposal --- .../proposals/syntax-tree-patterns.md | 575 ++++++++++++------ 1 file changed, 386 insertions(+), 189 deletions(-) diff --git a/book/src/development/proposals/syntax-tree-patterns.md b/book/src/development/proposals/syntax-tree-patterns.md index 9161986a7b77..c5587c4bf908 100644 --- a/book/src/development/proposals/syntax-tree-patterns.md +++ b/book/src/development/proposals/syntax-tree-patterns.md @@ -1,23 +1,22 @@ - - - Feature Name: syntax-tree-patterns - Start Date: 2019-03-12 -- RFC PR: (leave this empty) -- Rust Issue: (leave this empty) - -> Note: This project is part of my Master's Thesis (supervised by [@oli-obk](https://github.com/oli-obk)) +- RFC PR: [#3875](https://github.com/rust-lang/rust-clippy/pull/3875) # Summary -Introduce a domain-specific language (similar to regular expressions) that allows to describe lints using *syntax tree patterns*. +Introduce a domain-specific language (similar to regular expressions) that +allows to describe lints using *syntax tree patterns*. # Motivation +Finding parts of a syntax tree (AST, HIR, ...) that have certain properties +(e.g. "*an if that has a block as its condition*") is a major task when writing +lints. For non-trivial lints, it often requires nested pattern matching of AST / +HIR nodes. For example, testing that an expression is a boolean literal requires +the following checks: -Finding parts of a syntax tree (AST, HIR, ...) that have certain properties (e.g. "*an if that has a block as its condition*") is a major task when writing lints. For non-trivial lints, it often requires nested pattern matching of AST / HIR nodes. For example, testing that an expression is a boolean literal requires the following checks: - -``` +```rust if let ast::ExprKind::Lit(lit) = &expr.node { if let ast::LitKind::Bool(_) = &lit.node { ... @@ -25,9 +24,11 @@ if let ast::ExprKind::Lit(lit) = &expr.node { } ``` -Writing this kind of matching code quickly becomes a complex task and the resulting code is often hard to comprehend. The code below shows a simplified version of the pattern matching required by the `collapsible_if` lint: +Writing this kind of matching code quickly becomes a complex task and the +resulting code is often hard to comprehend. The code below shows a simplified +version of the pattern matching required by the `collapsible_if` lint: -``` +```rust // simplified version of the collapsible_if lint if let ast::ExprKind::If(check, then, None) = &expr.node { if then.stmts.len() == 1 { @@ -40,9 +41,10 @@ if let ast::ExprKind::If(check, then, None) = &expr.node { } ``` -The `if_chain` macro can improve readability by flattening the nested if statements, but the resulting code is still quite hard to read: +The `if_chain` macro can improve readability by flattening the nested if +statements, but the resulting code is still quite hard to read: -``` +```rust // simplified version of the collapsible_if lint if_chain! { if let ast::ExprKind::If(check, then, None) = &expr.node; @@ -55,39 +57,66 @@ if_chain! { } ``` -The code above matches if expressions that contain only another if expression (where both ifs don't have an else branch). While it's easy to explain what the lint does, it's hard to see that from looking at the code samples above. +The code above matches if expressions that contain only another if expression +(where both ifs don't have an else branch). While it's easy to explain what the +lint does, it's hard to see that from looking at the code samples above. -Following the motivation above, the first goal this RFC is to **simplify writing and reading lints**. +Following the motivation above, the first goal this RFC is to **simplify writing +and reading lints**. -The second part of the motivation is clippy's dependence on unstable compiler-internal data structures. Clippy lints are currently written against the compiler's AST / HIR which means that even small changes in these data structures might break a lot of lints. The second goal of this RFC is to **make lints independant of the compiler's AST / HIR data structures**. +The second part of the motivation is clippy's dependence on unstable +compiler-internal data structures. Clippy lints are currently written against +the compiler's AST / HIR which means that even small changes in these data +structures might break a lot of lints. The second goal of this RFC is to **make +lints independant of the compiler's AST / HIR data structures**. # Approach -A lot of complexity in writing lints currently seems to come from having to manually implement the matching logic (see code samples above). It's an imparative style that describes *how* to match a syntax tree node instead of specifying *what* should be matched against declaratively. In other areas, it's common to use declarative patterns to describe desired information and let the implementation do the actual matching. A well-known example of this approach are [regular expressions](https://en.wikipedia.org/wiki/Regular_expression). Instead of writing code that detects certain character sequences, one can describe a search pattern using a domain-specific language and search for matches using that pattern. The advantage of using a declarative domain-specific language is that its limited domain (e.g. matching character sequences in the case of regular expressions) allows to express entities in that domain in a very natural and expressive way. - -While regular expressions are very useful when searching for patterns in flat character sequences, they cannot easily be applied to hierarchical data structures like syntax trees. This RFC therefore proposes a pattern matching system that is inspired by regular expressions and designed for hierarchical syntax trees. +A lot of complexity in writing lints currently seems to come from having to +manually implement the matching logic (see code samples above). It's an +imparative style that describes *how* to match a syntax tree node instead of +specifying *what* should be matched against declaratively. In other areas, it's +common to use declarative patterns to describe desired information and let the +implementation do the actual matching. A well-known example of this approach are +[regular expressions](https://en.wikipedia.org/wiki/Regular_expression). Instead +of writing code that detects certain character sequences, one can describe a +search pattern using a domain-specific language and search for matches using +that pattern. The advantage of using a declarative domain-specific language is +that its limited domain (e.g. matching character sequences in the case of +regular expressions) allows to express entities in that domain in a very natural +and expressive way. + +While regular expressions are very useful when searching for patterns in flat +character sequences, they cannot easily be applied to hierarchical data +structures like syntax trees. This RFC therefore proposes a pattern matching +system that is inspired by regular expressions and designed for hierarchical +syntax trees. # Guide-level explanation -This proposal adds a `pattern!` macro that can be used to specify a syntax tree pattern to search for. A simple pattern is shown below: +This proposal adds a `pattern!` macro that can be used to specify a syntax tree +pattern to search for. A simple pattern is shown below: -``` +```rust pattern!{ - my_pattern: Expr = + my_pattern: Expr = Lit(Bool(false)) } ``` -This macro call defines a pattern named `my_pattern` that can be matched against an `Expr` syntax tree node. The actual pattern (`Lit(Bool(false))` in this case) defines which syntax trees should match the pattern. This pattern matches expressions that are boolean literals with value `false`. +This macro call defines a pattern named `my_pattern` that can be matched against +an `Expr` syntax tree node. The actual pattern (`Lit(Bool(false))` in this case) +defines which syntax trees should match the pattern. This pattern matches +expressions that are boolean literals with value `false`. The pattern can then be used to implement lints in the following way: -``` +```rust ... impl EarlyLintPass for MyAwesomeLint { fn check_expr(&mut self, cx: &EarlyContext, expr: &syntax::ast::Expr) { - + if my_pattern(expr).is_some() { cx.span_lint( MY_AWESOME_LINT, @@ -95,14 +124,18 @@ impl EarlyLintPass for MyAwesomeLint { "This is a match for a simple pattern. Well done!", ); } - + } } ``` -The `pattern!` macro call expands to a function `my_pattern` that expects a syntax tree expression as its argument and returns an `Option` that indicates whether the pattern matched. +The `pattern!` macro call expands to a function `my_pattern` that expects a +syntax tree expression as its argument and returns an `Option` that indicates +whether the pattern matched. -> Note: The result type is explained in more detail in [a later section](#the-result-type). For now, it's enough to know that the result is `Some` if the pattern matched and `None` otherwise. +> Note: The result type is explained in more detail in [a later +> section](#the-result-type). For now, it's enough to know that the result is +> `Some` if the pattern matched and `None` otherwise. ## Pattern syntax @@ -111,38 +144,43 @@ The following examples demonstate the pattern syntax: #### Any (`_`) -The simplest pattern is the any pattern. It matches anything and is therefore similar to regex's `*`. +The simplest pattern is the any pattern. It matches anything and is therefore +similar to regex's `*`. -``` +```rust pattern!{ // matches any expression - my_pattern: Expr = + my_pattern: Expr = _ } ``` #### Node (`()`) -Nodes are used to match a specific variant of an AST node. A node has a name and a number of arguments that depends on the node type. For example, the `Lit` node has a single argument that describes the type of the literal. As another example, the `If` node has three arguments describing the if's condition, then block and else block. +Nodes are used to match a specific variant of an AST node. A node has a name and +a number of arguments that depends on the node type. For example, the `Lit` node +has a single argument that describes the type of the literal. As another +example, the `If` node has three arguments describing the if's condition, then +block and else block. -``` +```rust pattern!{ // matches any expression that is a literal - my_pattern: Expr = + my_pattern: Expr = Lit(_) } pattern!{ // matches any expression that is a boolean literal - my_pattern: Expr = + my_pattern: Expr = Lit(Bool(_)) } pattern!{ // matches if expressions that have a boolean literal in their condition - // Note: The `_?` syntax here means that the else branch is optional and can be anything. + // Note: The `_?` syntax here means that the else branch is optional and can be anything. // This is discussed in more detail in the section `Repetition`. - my_pattern: Expr = + my_pattern: Expr = If( Lit(Bool(_)) , _, _?) } ``` @@ -152,69 +190,71 @@ pattern!{ A pattern can also contain Rust literals. These literals match themselves. -``` +```rust pattern!{ // matches the boolean literal false - my_pattern: Expr = + my_pattern: Expr = Lit(Bool(false)) } pattern!{ // matches the character literal 'x' - my_pattern: Expr = + my_pattern: Expr = Lit(Char('x')) } ``` #### Alternations (`a | b`) -``` +```rust pattern!{ // matches if the literal is a boolean or integer literal - my_pattern: Lit = + my_pattern: Lit = Bool(_) | Int(_) } pattern!{ // matches if the expression is a char literal with value 'x' or 'y' - my_pattern: Expr = + my_pattern: Expr = Lit( Char('x' | 'y') ) } ``` #### Empty (`()`) -The empty pattern represents an empty sequence or the `None` variant of an optional. +The empty pattern represents an empty sequence or the `None` variant of an +optional. -``` +```rust pattern!{ // matches if the expression is an empty array - my_pattern: Expr = + my_pattern: Expr = Array( () ) } pattern!{ // matches if expressions that don't have an else clause - my_pattern: Expr = + my_pattern: Expr = If(_, _, ()) } ``` #### Sequence (` `) -``` +```rust pattern!{ // matches the array [true, false] - my_pattern: Expr = + my_pattern: Expr = Array( Lit(Bool(true)) Lit(Bool(false)) ) } ``` #### Repetition (`*`, `+`, `?`, `{n}`, `{n,m}`, `{n,}`) -Elements may be repeated. The syntax for specifying repetitions is identical to [regex's syntax](https://docs.rs/regex/1.1.2/regex/#repetitions). +Elements may be repeated. The syntax for specifying repetitions is identical to +[regex's syntax](https://docs.rs/regex/1.1.2/regex/#repetitions). -``` +```rust pattern!{ // matches arrays that contain 2 'x's as their last or second-last elements // Examples: @@ -222,7 +262,7 @@ pattern!{ // ['x', 'x', 'y'] match // ['a', 'b', 'c', 'x', 'x', 'y'] match // ['x', 'x', 'y', 'z'] no match - my_pattern: Expr = + my_pattern: Expr = Array( _* Lit(Char('x')){2} _? ) } @@ -234,34 +274,35 @@ pattern!{ // If(_, _, _) | match | no match // If(_, _, _?) | match | match // If(_, _, ()) | no match | match - my_pattern: Expr = + my_pattern: Expr = If(_, _, _?) } ``` #### Named submatch (`#`) -``` +```rust pattern!{ // matches character literals and gives the literal the name foo - my_pattern: Expr = + my_pattern: Expr = Lit(Char(_)#foo) } pattern!{ // matches character literals and gives the char the name bar - my_pattern: Expr = + my_pattern: Expr = Lit(Char(_#bar)) } pattern!{ // matches character literals and gives the expression the name baz - my_pattern: Expr = + my_pattern: Expr = Lit(Char(_))#baz } ``` -The reason for using named submatches is described in the section [The result type](#the-result-type). +The reason for using named submatches is described in the section [The result +type](#the-result-type). ### Summary @@ -281,21 +322,31 @@ The following table gives an summary of the pattern syntax: ## The result type -A lot of lints require checks that go beyond what the pattern syntax described above can express. For example, a lint might want to check whether a node was created as part of a macro expansion or whether there's no comment above a node. Another example would be a lint that wants to match two nodes that have the same value (as needed by lints like `almost_swapped`). Instead of allowing users to write these checks into the pattern directly (which might make patterns hard to read), the proposed solution allows users to assign names to parts of a pattern expression. When matching a pattern against a syntax tree node, the return value will contain references to all nodes that were matched by these named subpatterns. This is similar to capture groups in regular expressions. +A lot of lints require checks that go beyond what the pattern syntax described +above can express. For example, a lint might want to check whether a node was +created as part of a macro expansion or whether there's no comment above a node. +Another example would be a lint that wants to match two nodes that have the same +value (as needed by lints like `almost_swapped`). Instead of allowing users to +write these checks into the pattern directly (which might make patterns hard to +read), the proposed solution allows users to assign names to parts of a pattern +expression. When matching a pattern against a syntax tree node, the return value +will contain references to all nodes that were matched by these named +subpatterns. This is similar to capture groups in regular expressions. For example, given the following pattern -``` +```rust pattern!{ // matches character literals - my_pattern: Expr = + my_pattern: Expr = Lit(Char(_#val_inner)#val)#val_outer } ``` -one could get references to the nodes that matched the subpatterns in the following way: +one could get references to the nodes that matched the subpatterns in the +following way: -``` +```rust ... fn check_expr(expr: &syntax::ast::Expr) { if let Some(result) = my_pattern(expr) { @@ -306,50 +357,57 @@ fn check_expr(expr: &syntax::ast::Expr) { } ``` -The types in the `result` struct depend on the pattern. For example, the following pattern +The types in the `result` struct depend on the pattern. For example, the +following pattern -``` +```rust pattern!{ // matches arrays of character literals - my_pattern_seq: Expr = + my_pattern_seq: Expr = Array( Lit(_)*#foo ) } ``` -matches arrays that consist of any number of literal expressions. Because those expressions are named `foo`, the result struct contains a `foo` attribute which is a vector of expressions: +matches arrays that consist of any number of literal expressions. Because those +expressions are named `foo`, the result struct contains a `foo` attribute which +is a vector of expressions: -``` +```rust ... if let Some(result) = my_pattern_seq(expr) { result.foo // type: Vec<&syntax::ast::Expr> } ``` -Another result type occurs when a name is only defined in one branch of an alternation: +Another result type occurs when a name is only defined in one branch of an +alternation: -``` +```rust pattern!{ // matches if expression is a boolean or integer literal - my_pattern_alt: Expr = + my_pattern_alt: Expr = Lit( Bool(_#bar) | Int(_) ) } ``` -In the pattern above, the `bar` name is only defined if the pattern matches a boolean literal. If it matches an integer literal, the name isn't set. To account fot this, the result struct's `bar` attribute is an option type: +In the pattern above, the `bar` name is only defined if the pattern matches a +boolean literal. If it matches an integer literal, the name isn't set. To +account for this, the result struct's `bar` attribute is an option type: -``` +```rust ... if let Some(result) = my_pattern_alt(expr) { result.bar // type: Option<&bool> } ``` -It's also possible to use a name in multiple alternation branches if they have compatible types: +It's also possible to use a name in multiple alternation branches if they have +compatible types: -``` +```rust pattern!{ // matches if expression is a boolean or integer literal - my_pattern_mult: Expr = + my_pattern_mult: Expr = Lit(_#baz) | Array( Lit(_#baz) ) } ... @@ -358,60 +416,77 @@ if let Some(result) = my_pattern_mult(expr) { } ``` -Named submatches are a **flat** namespace and this is intended. In the example above, two different sub-structures are assigned to a flat name. I expect that for most lints, a flat namespace is sufficient and easier to work with than a hierarchical one. +Named submatches are a **flat** namespace and this is intended. In the example +above, two different sub-structures are assigned to a flat name. I expect that +for most lints, a flat namespace is sufficient and easier to work with than a +hierarchical one. #### Two stages -Using named subpatterns, users can write lints in two stages. First, a coarse selection of possible matches is produced by the pattern syntax. In the second stage, the named subpattern references can be used to do additional tests like asserting that a node hasn't been created as part of a macro expansion. +Using named subpatterns, users can write lints in two stages. First, a coarse +selection of possible matches is produced by the pattern syntax. In the second +stage, the named subpattern references can be used to do additional tests like +asserting that a node hasn't been created as part of a macro expansion. ## Implementing clippy lints using patterns -As a "real-world" example, I re-implemented the `collapsible_if` lint using patterns. The code can be found [here](https://github.com/fkohlgrueber/rust-clippy-pattern/blob/039b07ecccaf96d6aa7504f5126720d2c9cceddd/clippy_lints/src/collapsible_if.rs#L88-L163). The pattern-based version passes all test cases that were written for `collapsible_if`. +As a "real-world" example, I re-implemented the `collapsible_if` lint using +patterns. The code can be found +[here](https://github.com/fkohlgrueber/rust-clippy-pattern/blob/039b07ecccaf96d6aa7504f5126720d2c9cceddd/clippy_lints/src/collapsible_if.rs#L88-L163). +The pattern-based version passes all test cases that were written for +`collapsible_if`. # Reference-level explanation ## Overview -The following diagram shows the dependencies between the main parts of the proposed solution: +The following diagram shows the dependencies between the main parts of the +proposed solution: ``` - Pattern syntax - | - | parsing / lowering - v - PatternTree - ^ - | - | - IsMatch trait - | - | - +---------------+-----------+---------+ - | | | | - v v v v - syntax::ast rustc::hir syn ... + Pattern syntax + | + | parsing / lowering + v + PatternTree + ^ + | + | + IsMatch trait + | + | + +---------------+-----------+---------+ + | | | | + v v v v + syntax::ast rustc::hir syn ... ``` -The pattern syntax described in the previous section is parsed / lowered into the so-called *PatternTree* data structure that represents a valid syntax tree pattern. Matching a *PatternTree* against an actual syntax tree (e.g. rust ast / hir or the syn ast, ...) is done using the *IsMatch* trait. +The pattern syntax described in the previous section is parsed / lowered into +the so-called *PatternTree* data structure that represents a valid syntax tree +pattern. Matching a *PatternTree* against an actual syntax tree (e.g. rust ast / +hir or the syn ast, ...) is done using the *IsMatch* trait. -The *PatternTree* and the *IsMatch* trait are introduced in more detail in the following sections. +The *PatternTree* and the *IsMatch* trait are introduced in more detail in the +following sections. ## PatternTree -The core data structure of this RFC is the **PatternTree**. +The core data structure of this RFC is the **PatternTree**. -It's a data structure similar to rust's AST / HIR, but with the following differences: +It's a data structure similar to rust's AST / HIR, but with the following +differences: - The PatternTree doesn't contain parsing information like `Span`s - The PatternTree can represent alternatives, sequences and optionals The code below shows a simplified version of the current PatternTree: -> Note: The current implementation can be found [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/pattern_tree.rs#L50-L96). +> Note: The current implementation can be found +> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/pattern_tree.rs#L50-L96). -``` +```rust pub enum Expr { Lit(Alt), Array(Seq), @@ -441,9 +516,10 @@ pub enum BlockType { The `Alt`, `Seq` and `Opt` structs look like these: -> Note: The current implementation can be found [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60). +> Note: The current implementation can be found +> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60). -``` +```rust pub enum Alt { Any, Elmt(Box), @@ -477,27 +553,45 @@ pub struct RepeatRange { ## Parsing / Lowering -The input of a `pattern!` macro call is parsed into a `ParseTree` first and then lowered to a `PatternTree`. +The input of a `pattern!` macro call is parsed into a `ParseTree` first and then +lowered to a `PatternTree`. -Valid patterns depend on the *PatternTree* definitions. For example, the pattern `Lit(Bool(_)*)` isn't valid because the parameter type of the `Lit` variant of the `Expr` enum is `Any` and therefore doesn't support repetition (`*`). As another example, `Array( Lit(_)* )` is a valid pattern because the parameter of `Array` is of type `Seq` which allows sequences and repetitions. +Valid patterns depend on the *PatternTree* definitions. For example, the pattern +`Lit(Bool(_)*)` isn't valid because the parameter type of the `Lit` variant of +the `Expr` enum is `Any` and therefore doesn't support repetition (`*`). As +another example, `Array( Lit(_)* )` is a valid pattern because the parameter of +`Array` is of type `Seq` which allows sequences and repetitions. -> Note: names in the pattern syntax correspond to *PatternTree* enum **variants**. For example, the `Lit` in the pattern above refers to the `Lit` variant of the `Expr` enum (`Expr::Lit`), not the `Lit` enum. +> Note: names in the pattern syntax correspond to *PatternTree* enum +> **variants**. For example, the `Lit` in the pattern above refers to the `Lit` +> variant of the `Expr` enum (`Expr::Lit`), not the `Lit` enum. ## The IsMatch Trait -The pattern syntax and the *PatternTree* are independant of specific syntax tree implementations (rust ast / hir, syn, ...). When looking at the different pattern examples in the previous sections, it can be seen that the patterns don't contain any information specific to a certain syntax tree implementation. In contrast, clippy lints currently match against ast / hir syntax tree nodes and therefore directly depend on their implementation. +The pattern syntax and the *PatternTree* are independant of specific syntax tree +implementations (rust ast / hir, syn, ...). When looking at the different +pattern examples in the previous sections, it can be seen that the patterns +don't contain any information specific to a certain syntax tree implementation. +In contrast, clippy lints currently match against ast / hir syntax tree nodes +and therefore directly depend on their implementation. -The connection between the *PatternTree* and specific syntax tree implementations is the `IsMatch` trait. It defines how to match *PatternTree* nodes against specific syntax tree nodes. A simplified implementation of the `IsMatch` trait is shown below: +The connection between the *PatternTree* and specific syntax tree +implementations is the `IsMatch` trait. It defines how to match *PatternTree* +nodes against specific syntax tree nodes. A simplified implementation of the +`IsMatch` trait is shown below: -``` +```rust pub trait IsMatch { fn is_match(&self, other: &'o O) -> bool; } ``` -This trait needs to be implemented on each enum of the *PatternTree* (for the corresponding syntax tree types). For example, the `IsMatch` implementation for matching `ast::LitKind` against the *PatternTree's* `Lit` enum might look like this: +This trait needs to be implemented on each enum of the *PatternTree* (for the +corresponding syntax tree types). For example, the `IsMatch` implementation for +matching `ast::LitKind` against the *PatternTree's* `Lit` enum might look like +this: -``` +```rust impl IsMatch for Lit { fn is_match(&self, other: &ast::LitKind) -> bool { match (self, other) { @@ -510,25 +604,30 @@ impl IsMatch for Lit { } ``` -All `IsMatch` implementations for matching the current *PatternTree* against `syntax::ast` can be found [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/ast_match.rs). +All `IsMatch` implementations for matching the current *PatternTree* against +`syntax::ast` can be found +[here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/ast_match.rs). # Drawbacks #### Performance -The pattern matching code is currently not optimized for performance, so it might be slower than hand-written matching code. -Additionally, the two-stage approach (matching against the coarse pattern first and checking for additional properties later) might be slower than the current practice of checking for structure and additional properties in one pass. For example, the following lint +The pattern matching code is currently not optimized for performance, so it +might be slower than hand-written matching code. Additionally, the two-stage +approach (matching against the coarse pattern first and checking for additional +properties later) might be slower than the current practice of checking for +structure and additional properties in one pass. For example, the following lint -``` +```rust pattern!{ - pat_if_without_else: Expr = + pat_if_without_else: Expr = If( _, Block( Expr( If(_, _, ())#inner ) - | Semi( If(_, _, ())#inner ) - )#then, + | Semi( If(_, _, ())#inner ) + )#then, () ) } @@ -541,9 +640,11 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { } ``` -first matches against the pattern and then checks that the `then` block doesn't start with a comment. Using clippy's current approach, it's possible to check for these conditions earlier: +first matches against the pattern and then checks that the `then` block doesn't +start with a comment. Using clippy's current approach, it's possible to check +for these conditions earlier: -``` +```rust fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { if_chain! { if let ast::ExprKind::If(ref check, ref then, None) = expr.node; @@ -557,30 +658,57 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { } ``` -Whether or not this causes performance regressions depends on actual patterns. If it turns out to be a problem, the pattern matching algorithms could be extended to allow "early filtering" (see the [Early Filtering](#early-filtering) section in Future Possibilities). +Whether or not this causes performance regressions depends on actual patterns. +If it turns out to be a problem, the pattern matching algorithms could be +extended to allow "early filtering" (see the [Early Filtering](#early-filtering) +section in Future Possibilities). -That being said, I don't see any conceptual limitations regarding pattern matching performance. +That being said, I don't see any conceptual limitations regarding pattern +matching performance. #### Applicability -Even though I'd expect that a lot of lints can be written using the proposed pattern syntax, it's unlikely that all lints can be expressed using patterns. I suspect that there will still be lints that need to be implemented by writing custom pattern matching code. This would lead to mix within clippy's codebase where some lints are implemented using patterns and others aren't. This inconsistency might be considered a drawback. +Even though I'd expect that a lot of lints can be written using the proposed +pattern syntax, it's unlikely that all lints can be expressed using patterns. I +suspect that there will still be lints that need to be implemented by writing +custom pattern matching code. This would lead to mix within clippy's codebase +where some lints are implemented using patterns and others aren't. This +inconsistency might be considered a drawback. # Rationale and alternatives -Specifying lints using syntax tree patterns has a couple of advantages compared to the current approach of manually writing matching code. First, syntax tree patterns allow users to describe patterns in a simple and expressive way. This makes it easier to write new lints for both novices and experts and also makes reading / modifying existing lints simpler. - -Another advantage is that lints are independent of specific syntax tree implementations (e.g. AST / HIR, ...). When these syntax tree implementations change, only the `IsMatch` trait implementations need to be adapted and existing lints can remain unchanged. This also means that if the `IsMatch` trait implementations were integrated into the compiler, updating the `IsMatch` implementations would be required for the compiler to compile successfully. This could reduce the number of times clippy breaks because of changes in the compiler. Another advantage of the pattern's independence is that converting an `EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole pattern matching code. In fact, the pattern might work just fine without any adaptions. - +Specifying lints using syntax tree patterns has a couple of advantages compared +to the current approach of manually writing matching code. First, syntax tree +patterns allow users to describe patterns in a simple and expressive way. This +makes it easier to write new lints for both novices and experts and also makes +reading / modifying existing lints simpler. + +Another advantage is that lints are independent of specific syntax tree +implementations (e.g. AST / HIR, ...). When these syntax tree implementations +change, only the `IsMatch` trait implementations need to be adapted and existing +lints can remain unchanged. This also means that if the `IsMatch` trait +implementations were integrated into the compiler, updating the `IsMatch` +implementations would be required for the compiler to compile successfully. This +could reduce the number of times clippy breaks because of changes in the +compiler. Another advantage of the pattern's independence is that converting an +`EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole +pattern matching code. In fact, the pattern might work just fine without any +adaptions. ## Alternatives ### Rust-like pattern syntax -The proposed pattern syntax requires users to know the structure of the `PatternTree` (which is very similar to the AST's / HIR's structure) and also the pattern syntax. An alternative would be to introduce a pattern syntax that is similar to actual Rust syntax (probably like the `quote!` macro). For example, a pattern that matches `if` expressions that have `false` in their condition could look like this: +The proposed pattern syntax requires users to know the structure of the +`PatternTree` (which is very similar to the AST's / HIR's structure) and also +the pattern syntax. An alternative would be to introduce a pattern syntax that +is similar to actual Rust syntax (probably like the `quote!` macro). For +example, a pattern that matches `if` expressions that have `false` in their +condition could look like this: -``` +```rust if false { #[*] } @@ -588,9 +716,13 @@ if false { #### Problems -Extending Rust syntax (which is quite complex by itself) with additional syntax needed for specifying patterns (alternations, sequences, repetisions, named submatches, ...) might become difficult to read and really hard to parse properly. +Extending Rust syntax (which is quite complex by itself) with additional syntax +needed for specifying patterns (alternations, sequences, repetisions, named +submatches, ...) might become difficult to read and really hard to parse +properly. -For example, a pattern that matches a binary operation that has `0` on both sides might look like this: +For example, a pattern that matches a binary operation that has `0` on both +sides might look like this: ``` 0 #[*:BinOpKind] 0 @@ -602,74 +734,116 @@ Now consider this slightly more complex example: 1 + 0 #[*:BinOpKind] 0 ``` -The parser would need to know the precedence of `#[*:BinOpKind]` because it affects the structure of the resulting AST. `1 + 0 + 0` is parsed as `(1 + 0) + 0` while `1 + 0 * 0` is parsed as `1 + (0 * 0)`. Since the pattern could be any `BinOpKind`, the precedence cannot be known in advance. +The parser would need to know the precedence of `#[*:BinOpKind]` because it +affects the structure of the resulting AST. `1 + 0 + 0` is parsed as `(1 + 0) + +0` while `1 + 0 * 0` is parsed as `1 + (0 * 0)`. Since the pattern could be any +`BinOpKind`, the precedence cannot be known in advance. -Another example of a problem would be named submatches. Take a look at this pattern: +Another example of a problem would be named submatches. Take a look at this +pattern: -``` +```rust fn test() { 1 #foo } ``` -Which node is `#foo` referring to? `int`, `ast::Lit`, `ast::Expr`, `ast::Stmt`? Naming subpatterns in a rust-like syntax is difficult because a lot of AST nodes don't have a syntactic element that can be used to put the name tag on. In these situations, the only sensible option would be to assign the name tag to the outermost node (`ast::Stmt` in the example above), because the information of all child nodes can be retrieved through the outermost node. The problem with this then would be that accessing inner nodes (like `ast::Lit`) would again require manual pattern matching. +Which node is `#foo` referring to? `int`, `ast::Lit`, `ast::Expr`, `ast::Stmt`? +Naming subpatterns in a rust-like syntax is difficult because a lot of AST nodes +don't have a syntactic element that can be used to put the name tag on. In these +situations, the only sensible option would be to assign the name tag to the +outermost node (`ast::Stmt` in the example above), because the information of +all child nodes can be retrieved through the outermost node. The problem with +this then would be that accessing inner nodes (like `ast::Lit`) would again +require manual pattern matching. -In general, Rust syntax contains a lot of code structure implicitly. This structure is reconstructed during parsing (e.g. binary operations are reconstructed using operator precedence and left-to-right) and is one of the reasons why parsing is a complex task. The advantage of this approach is that writing code is simpler for users. +In general, Rust syntax contains a lot of code structure implicitly. This +structure is reconstructed during parsing (e.g. binary operations are +reconstructed using operator precedence and left-to-right) and is one of the +reasons why parsing is a complex task. The advantage of this approach is that +writing code is simpler for users. -When writing *syntax tree patterns*, each element of the hierarchy might have alternatives, repetitions, etc.. Respecting that while still allowing human-friendly syntax that contains structure implicitly seems to be really complex, if not impossible. +When writing *syntax tree patterns*, each element of the hierarchy might have +alternatives, repetitions, etc.. Respecting that while still allowing +human-friendly syntax that contains structure implicitly seems to be really +complex, if not impossible. -Developing such a syntax would also require to maintain a custom parser that is at least as complex as the Rust parser itself. Additionally, future changes in the Rust syntax might be incompatible with such a syntax. +Developing such a syntax would also require to maintain a custom parser that is +at least as complex as the Rust parser itself. Additionally, future changes in +the Rust syntax might be incompatible with such a syntax. -In summary, I think that developing such a syntax would introduce a lot of complexity to solve a relatively minor problem. +In summary, I think that developing such a syntax would introduce a lot of +complexity to solve a relatively minor problem. -The issue of users not knowing about the *PatternTree* structure could be solved by a tool that, given a rust program, generates a pattern that matches only this program (similar to the clippy author lint). +The issue of users not knowing about the *PatternTree* structure could be solved +by a tool that, given a rust program, generates a pattern that matches only this +program (similar to the clippy author lint). -For some simple cases (like the first example above), it might be possible to successfully mix Rust and pattern syntax. This space could be further explored in a future extension. +For some simple cases (like the first example above), it might be possible to +successfully mix Rust and pattern syntax. This space could be further explored +in a future extension. # Prior art -The pattern syntax is heavily inspired by regular expressions (repetitions, alternatives, sequences, ...). +The pattern syntax is heavily inspired by regular expressions (repetitions, +alternatives, sequences, ...). -From what I've seen until now, other linters also implement lints that directly work on syntax tree data structures, just like clippy does currently. I would therefore consider the pattern syntax to be *new*, but please correct me if I'm wrong. +From what I've seen until now, other linters also implement lints that directly +work on syntax tree data structures, just like clippy does currently. I would +therefore consider the pattern syntax to be *new*, but please correct me if I'm +wrong. # Unresolved questions #### How to handle multiple matches? -When matching a syntax tree node against a pattern, there are possibly multiple ways in which the pattern can be matched. A simple example of this would be the following pattern: +When matching a syntax tree node against a pattern, there are possibly multiple +ways in which the pattern can be matched. A simple example of this would be the +following pattern: -``` +```rust pattern!{ - my_pattern: Expr = + my_pattern: Expr = Array( _* Lit(_)+#literals) } ``` -This pattern matches arrays that end with at least one literal. Now given the array `[x, 1, 2]`, should `1` be matched as part of the `_*` or the `Lit(_)+` part of the pattern? The difference is important because the named submatch `#literals` would contain 1 or 2 elements depending how the pattern is matched. In regular expressions, this problem is solved by matching "greedy" by default and "non-greedy" optionally. +This pattern matches arrays that end with at least one literal. Now given the +array `[x, 1, 2]`, should `1` be matched as part of the `_*` or the `Lit(_)+` +part of the pattern? The difference is important because the named submatch +`#literals` would contain 1 or 2 elements depending how the pattern is matched. +In regular expressions, this problem is solved by matching "greedy" by default +and "non-greedy" optionally. -I haven't looked much into this yet because I don't know how relevant it is for most lints. The current implementation simply returns the first match it finds. +I haven't looked much into this yet because I don't know how relevant it is for +most lints. The current implementation simply returns the first match it finds. # Future possibilities #### Implement rest of Rust Syntax -The current project only implements a small part of the Rust syntax. In the future, this should incrementally be extended to more syntax to allow implementing more lints. Implementing more of the Rust syntax requires extending the `PatternTree` and `IsMatch` implementations, but should be relatively straight-forward. +The current project only implements a small part of the Rust syntax. In the +future, this should incrementally be extended to more syntax to allow +implementing more lints. Implementing more of the Rust syntax requires extending +the `PatternTree` and `IsMatch` implementations, but should be relatively +straight-forward. #### Early filtering -As described in the *Drawbacks/Performance* section, allowing additional checks during the pattern matching might be beneficial. +As described in the *Drawbacks/Performance* section, allowing additional checks +during the pattern matching might be beneficial. The pattern below shows how this could look like: -``` +```rust pattern!{ - pat_if_without_else: Expr = + pat_if_without_else: Expr = If( _, Block( Expr( If(_, _, ())#inner ) - | Semi( If(_, _, ())#inner ) - )#then, + | Semi( If(_, _, ())#inner ) + )#then, () ) where @@ -677,47 +851,64 @@ pattern!{ } ``` -The difference compared to the currently proposed two-stage filtering is that using early filtering, the condition (`!in_macro(#then.span)` in this case) would be evaluated as soon as the `Block(_)#then` was matched. +The difference compared to the currently proposed two-stage filtering is that +using early filtering, the condition (`!in_macro(#then.span)` in this case) +would be evaluated as soon as the `Block(_)#then` was matched. -Another idea in this area would be to introduce a syntax for backreferences. They could be used to require that multiple parts of a pattern should match the same value. For example, the `assign_op_pattern` lint that searches for `a = a op b` and recommends changing it to `a op= b` requires that both occurrances of `a` are the same. Using `=#...` as syntax for backreferences, the lint could be implemented like this: +Another idea in this area would be to introduce a syntax for backreferences. +They could be used to require that multiple parts of a pattern should match the +same value. For example, the `assign_op_pattern` lint that searches for `a = a +op b` and recommends changing it to `a op= b` requires that both occurrances of +`a` are the same. Using `=#...` as syntax for backreferences, the lint could be +implemented like this: -``` +```rust pattern!{ - assign_op_pattern: Expr = + assign_op_pattern: Expr = Assign(_#target, Binary(_, =#target, _) } ``` #### Match descendant -A lot of lints currently implement custom visitors that check whether any subtree (which might not be a direct descendant) of the current node matches some properties. This cannot be expressed with the proposed pattern syntax. Extending the pattern syntax to allow patterns like "a function that contains at least two return statements" could be a practical addition. +A lot of lints currently implement custom visitors that check whether any +subtree (which might not be a direct descendant) of the current node matches +some properties. This cannot be expressed with the proposed pattern syntax. +Extending the pattern syntax to allow patterns like "a function that contains at +least two return statements" could be a practical addition. #### Negation operator for alternatives -For patterns like "a literal that is not a boolean literal" one currently needs to list all alternatives except the boolean case. Introducing a negation operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three literal types are implemented). +For patterns like "a literal that is not a boolean literal" one currently needs +to list all alternatives except the boolean case. Introducing a negation +operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern +would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three +literal types are implemented). #### Functional composition -Patterns currently don't have any concept of composition. This leads to repetitions within patterns. For example, one of the collapsible-if patterns currently has to be written like this: +Patterns currently don't have any concept of composition. This leads to +repetitions within patterns. For example, one of the collapsible-if patterns +currently has to be written like this: -``` +```rust pattern!{ - pat_if_else: Expr = + pat_if_else: Expr = If( - _, - _, + _, + _, Block_( Block( - Expr((If(_, _, _?) | IfLet(_, _?))#else_) | + Expr((If(_, _, _?) | IfLet(_, _?))#else_) | Semi((If(_, _, _?) | IfLet(_, _?))#else_) )#block_inner )#block ) | IfLet( - _, + _, Block_( Block( - Expr((If(_, _, _?) | IfLet(_, _?))#else_) | + Expr((If(_, _, _?) | IfLet(_, _?))#else_) | Semi((If(_, _, _?) | IfLet(_, _?))#else_) )#block_inner )#block @@ -725,9 +916,10 @@ pattern!{ } ``` -If patterns supported defining functions of subpatterns, the code could be simplified as follows: +If patterns supported defining functions of subpatterns, the code could be +simplified as follows: -``` +```rust pattern!{ fn expr_or_semi(expr: Expr) -> Stmt { Expr(expr) | Semi(expr) @@ -735,7 +927,7 @@ pattern!{ fn if_or_if_let(then: Block, else: Opt) -> Expr { If(_, then, else) | IfLet(then, else) } - pat_if_else: Expr = + pat_if_else: Expr = if_or_if_let( _, Block_( @@ -747,43 +939,48 @@ pattern!{ } ``` -Additionally, common patterns like `expr_or_semi` could be shared between different lints. +Additionally, common patterns like `expr_or_semi` could be shared between +different lints. #### Clippy Pattern Author -Another improvement could be to create a tool that, given some valid Rust syntax, generates a pattern that matches this syntax exactly. This would make starting to write a pattern easier. A user could take a look at the patterns generated for a couple of Rust code examples and use that information to write a pattern that matches all of them. +Another improvement could be to create a tool that, given some valid Rust +syntax, generates a pattern that matches this syntax exactly. This would make +starting to write a pattern easier. A user could take a look at the patterns +generated for a couple of Rust code examples and use that information to write a +pattern that matches all of them. This is similar to clippy's author lint. #### Supporting other syntaxes -Most of the proposed system is language-agnostic. For example, the pattern syntax could also be used to describe patterns for other programming languages. +Most of the proposed system is language-agnostic. For example, the pattern +syntax could also be used to describe patterns for other programming languages. -In order to support other languages' syntaxes, one would need to implement another `PatternTree` that sufficiently describes the languages' AST and implement `IsMatch` for this `PatternTree` and the languages' AST. +In order to support other languages' syntaxes, one would need to implement +another `PatternTree` that sufficiently describes the languages' AST and +implement `IsMatch` for this `PatternTree` and the languages' AST. -One aspect of this is that it would even be possible to write lints that work on the pattern syntax itself. For example, when writing the following pattern +One aspect of this is that it would even be possible to write lints that work on +the pattern syntax itself. For example, when writing the following pattern -``` +```rust pattern!{ - my_pattern: Expr = + my_pattern: Expr = Array( Lit(Bool(false)) Lit(Bool(false)) ) } ``` -a lint that works on the pattern syntax's AST could suggest using this pattern instead: +a lint that works on the pattern syntax's AST could suggest using this pattern +instead: -``` +```rust pattern!{ - my_pattern: Expr = + my_pattern: Expr = Array( Lit(Bool(false)){2} ) } ``` -In the future, clippy could use this system to also provide lints for custom syntaxes like those found in macros. - -# Final Note - -This is the first RFC I've ever written and it might be a little rough around the edges. - -I'm looking forward to hearing your feedback and discussing the proposal. +In the future, clippy could use this system to also provide lints for custom +syntaxes like those found in macros. From 5610d22c8d58d12748308efb6e331e65618d11dc Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 25 Nov 2022 16:36:22 +0100 Subject: [PATCH 263/524] Re-enable `uninlined_format_args` on multiline `format!` But do not display the code suggestion which can be sometimes completely broken (fortunately when applied it's valid) --- clippy_lints/src/format_args.rs | 19 ++++++++++------ tests/ui/uninlined_format_args.fixed | 11 +++------- tests/ui/uninlined_format_args.stderr | 31 ++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index ec45be558f14..fd3ce2f3d6cd 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -9,7 +9,10 @@ use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; use itertools::Itertools; -use rustc_errors::Applicability; +use rustc_errors::{ + Applicability, + SuggestionStyle::{CompletelyHidden, ShowCode}, +}; use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; @@ -286,10 +289,9 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si return; } - // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 - if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) { - return; - } + // multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 + // in those cases, make the code suggestion hidden + let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)); span_lint_and_then( cx, @@ -297,7 +299,12 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si call_site, "variables can be used directly in the `format!` string", |diag| { - diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable); + diag.multipart_suggestion_with_style( + "change this to", + fixes, + Applicability::MachineApplicable, + if multiline_fix { CompletelyHidden } else { ShowCode }, + ); }, ); } diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index 106274479751..ca56c95c23f4 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -44,9 +44,7 @@ fn tester(fn_arg: i32) { println!("val='{local_i32}'"); // space+tab println!("val='{local_i32}'"); // tab+space println!( - "val='{ - }'", - local_i32 + "val='{local_i32}'" ); println!("{local_i32}"); println!("{fn_arg}"); @@ -110,8 +108,7 @@ fn tester(fn_arg: i32) { println!("{local_f64:width$.prec$}"); println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); println!( - "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", - local_i32, width, prec, + "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", ); println!( "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", @@ -142,9 +139,7 @@ fn tester(fn_arg: i32) { println!(no_param_str!(), local_i32); println!( - "{}", - // comment with a comma , in it - val, + "{val}", ); println!("{val}"); diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index 2ce3b7fa960c..1182d57ce9b7 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -59,6 +59,16 @@ LL - println!("val='{ }'", local_i32); // tab+space LL + println!("val='{local_i32}'"); // tab+space | +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:46:5 + | +LL | / println!( +LL | | "val='{ +LL | | }'", +LL | | local_i32 +LL | | ); + | |_____^ + error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:51:5 | @@ -767,6 +777,15 @@ LL - println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); | +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:112:5 + | +LL | / println!( +LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", +LL | | local_i32, width, prec, +LL | | ); + | |_____^ + error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:123:5 | @@ -815,6 +834,16 @@ LL - println!("{}", format!("{}", local_i32)); LL + println!("{}", format!("{local_i32}")); | +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:144:5 + | +LL | / println!( +LL | | "{}", +LL | | // comment with a comma , in it +LL | | val, +LL | | ); + | |_____^ + error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:149:5 | @@ -875,5 +904,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 73 previous errors +error: aborting due to 76 previous errors From 2fd10bc59b2c4c39691a1a9ec9de318a01cbf60c Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 25 Nov 2022 16:41:08 +0100 Subject: [PATCH 264/524] dogfood with expanded `uninlined_format_args` --- clippy_utils/src/ty.rs | 15 +++++---------- lintcheck/src/main.rs | 15 +++++++-------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 897edfc5495f..09967f317f89 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -1005,14 +1005,12 @@ pub fn make_projection<'tcx>( debug_assert!( generic_count == substs.len(), - "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\ + "wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\ note: the expected parameters are: {:#?}\n\ - the given arguments are: `{:#?}`", + the given arguments are: `{substs:#?}`", assoc_item.def_id, substs.len(), - generic_count, params.map(GenericParamDefKind::descr).collect::>(), - substs, ); if let Some((idx, (param, arg))) = params @@ -1030,14 +1028,11 @@ pub fn make_projection<'tcx>( { debug_assert!( false, - "mismatched subst type at index {}: expected a {}, found `{:?}`\n\ + "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\ note: the expected parameters are {:#?}\n\ - the given arguments are {:#?}", - idx, + the given arguments are {substs:#?}", param.descr(), - arg, - params.map(GenericParamDefKind::descr).collect::>(), - substs, + params.map(GenericParamDefKind::descr).collect::>() ); } } diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index ee8ab7c1d7cb..bd49f0960726 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -120,8 +120,8 @@ impl ClippyWarning { format!("$CARGO_HOME/{}", stripped.display()) } else { format!( - "target/lintcheck/sources/{}-{}/{}", - crate_name, crate_version, span.file_name + "target/lintcheck/sources/{crate_name}-{crate_version}/{}", + span.file_name ) }; @@ -322,13 +322,13 @@ impl Crate { if config.max_jobs == 1 { println!( - "{}/{} {}% Linting {} {}", - index, total_crates_to_lint, perc, &self.name, &self.version + "{index}/{total_crates_to_lint} {perc}% Linting {} {}", + &self.name, &self.version ); } else { println!( - "{}/{} {}% Linting {} {} in target dir {:?}", - index, total_crates_to_lint, perc, &self.name, &self.version, thread_index + "{index}/{total_crates_to_lint} {perc}% Linting {} {} in target dir {thread_index:?}", + &self.name, &self.version ); } @@ -398,8 +398,7 @@ impl Crate { .output() .unwrap_or_else(|error| { panic!( - "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n", - error, + "Encountered error:\n{error:?}\ncargo_clippy_path: {}\ncrate path:{}\n", &cargo_clippy_path.display(), &self.path.display() ); From 93b5c893e6394104b5ae80cf42d7220ad3d18779 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 10 Nov 2022 13:19:15 +0100 Subject: [PATCH 265/524] Add semicolon-outside/inside-block lints --- CHANGELOG.md | 2 + clippy_lints/src/declared_lints.rs | 2 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/semicolon_block.rs | 131 ++++++++++++++++++++++++ tests/ui/semicolon_inside_block.fixed | 83 +++++++++++++++ tests/ui/semicolon_inside_block.rs | 83 +++++++++++++++ tests/ui/semicolon_inside_block.stderr | 39 +++++++ tests/ui/semicolon_outside_block.fixed | 83 +++++++++++++++ tests/ui/semicolon_outside_block.rs | 83 +++++++++++++++ tests/ui/semicolon_outside_block.stderr | 39 +++++++ 10 files changed, 547 insertions(+) create mode 100644 clippy_lints/src/semicolon_block.rs create mode 100644 tests/ui/semicolon_inside_block.fixed create mode 100644 tests/ui/semicolon_inside_block.rs create mode 100644 tests/ui/semicolon_inside_block.stderr create mode 100644 tests/ui/semicolon_outside_block.fixed create mode 100644 tests/ui/semicolon_outside_block.rs create mode 100644 tests/ui/semicolon_outside_block.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b6b12c623af..dac4a54b11fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4353,6 +4353,8 @@ Released 2018-09-13 [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors [`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned +[`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block +[`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block [`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index eb3210946f11..76b4a2ccf5e1 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -525,6 +525,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::returns::NEEDLESS_RETURN_INFO, crate::same_name_method::SAME_NAME_METHOD_INFO, crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO, + crate::semicolon_block::SEMICOLON_INSIDE_BLOCK_INFO, + crate::semicolon_block::SEMICOLON_OUTSIDE_BLOCK_INFO, crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO, crate::serde_api::SERDE_API_MISUSE_INFO, crate::shadow::SHADOW_REUSE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 17dbd983b6c0..cbca317a1463 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -257,6 +257,7 @@ mod return_self_not_must_use; mod returns; mod same_name_method; mod self_named_constructors; +mod semicolon_block; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; @@ -884,6 +885,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); + store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs new file mode 100644 index 000000000000..911d38f65d6c --- /dev/null +++ b/clippy_lints/src/semicolon_block.rs @@ -0,0 +1,131 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::{get_parent_expr_for_hir, get_parent_node}; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, Node, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// + /// For () returning expressions, check that the semicolon is inside the block. + /// + /// ### Why is this bad? + /// + /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine and this lint suggests inside the block. + /// Take a look at `semicolon_outside_block` for the other alternative. + /// + /// ### Example + /// + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x) }; + /// ``` + /// Use instead: + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x); } + /// ``` + #[clippy::version = "1.66.0"] + pub SEMICOLON_INSIDE_BLOCK, + restriction, + "add a semicolon inside the block" +} +declare_clippy_lint! { + /// ### What it does + /// + /// For () returning expressions, check that the semicolon is outside the block. + /// + /// ### Why is this bad? + /// + /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine and this lint suggests outside the block. + /// Take a look at `semicolon_inside_block` for the other alternative. + /// + /// ### Example + /// + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x); } + /// ``` + /// Use instead: + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x) }; + /// ``` + #[clippy::version = "1.66.0"] + pub SEMICOLON_OUTSIDE_BLOCK, + restriction, + "add a semicolon outside the block" +} +declare_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]); + +impl LateLintPass<'_> for SemicolonBlock { + fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { + semicolon_inside_block(cx, block); + semicolon_outside_block(cx, block); + } +} + +fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>) { + if !block.span.from_expansion() + && let Some(tail) = block.expr + && let Some(block_expr @ Expr { kind: ExprKind::Block(_, _), ..}) = get_parent_expr_for_hir(cx, block.hir_id) + && let Some(Node::Stmt(Stmt { kind: StmtKind::Semi(_), span, .. })) = get_parent_node(cx.tcx, block_expr.hir_id) + { + let expr_snip = snippet_with_macro_callsite(cx, tail.span, ".."); + + let mut suggestion: String = snippet_with_macro_callsite(cx, block.span, "..").to_string(); + + if let Some((expr_offset, _)) = suggestion.rmatch_indices(&*expr_snip).next() { + suggestion.insert(expr_offset + expr_snip.len(), ';'); + } else { + return; + } + + span_lint_and_sugg( + cx, + SEMICOLON_INSIDE_BLOCK, + *span, + "consider moving the `;` inside the block for consistent formatting", + "put the `;` here", + suggestion, + Applicability::MaybeIncorrect, + ); + } +} + +fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>) { + if !block.span.from_expansion() + && block.expr.is_none() + && let [.., Stmt { kind: StmtKind::Semi(expr), .. }] = block.stmts + && let Some(block_expr @ Expr { kind: ExprKind::Block(_, _), ..}) = get_parent_expr_for_hir(cx,block.hir_id) + && let Some(Node::Stmt(Stmt { kind: StmtKind::Expr(_), .. })) = get_parent_node(cx.tcx, block_expr.hir_id) { + let expr_snip = snippet_with_macro_callsite(cx, expr.span, ".."); + + let mut suggestion: String = snippet_with_macro_callsite(cx, block.span, "..").to_string(); + + if let Some((expr_offset, _)) = suggestion.rmatch_indices(&*expr_snip).next() + && let Some(semi_offset) = suggestion[expr_offset + expr_snip.len()..].find(';') { + suggestion.remove(expr_offset + expr_snip.len() + semi_offset); + } else { + return; + } + + suggestion.push(';'); + + span_lint_and_sugg( + cx, + SEMICOLON_OUTSIDE_BLOCK, + block.span, + "consider moving the `;` outside the block for consistent formatting", + "put the `;` outside the block", + suggestion, + Applicability::MaybeIncorrect, + ); + } +} diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed new file mode 100644 index 000000000000..4cd112dd5e12 --- /dev/null +++ b/tests/ui/semicolon_inside_block.fixed @@ -0,0 +1,83 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_inside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()); } + { m!(()); } + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_inside_block.rs b/tests/ui/semicolon_inside_block.rs new file mode 100644 index 000000000000..7512125c051d --- /dev/null +++ b/tests/ui/semicolon_inside_block.rs @@ -0,0 +1,83 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_inside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()) }; + { m!(()); } + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_inside_block.stderr b/tests/ui/semicolon_inside_block.stderr new file mode 100644 index 000000000000..febe74b49bda --- /dev/null +++ b/tests/ui/semicolon_inside_block.stderr @@ -0,0 +1,39 @@ +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:39:5 + | +LL | { unit_fn_block() }; + | ^^^^^^^^^^^^^^^^^^^^ help: put the `;` here: `{ unit_fn_block(); }` + | + = note: `-D clippy::semicolon-inside-block` implied by `-D warnings` + +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:40:5 + | +LL | unsafe { unit_fn_block() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: put the `;` here: `unsafe { unit_fn_block(); }` + +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:48:5 + | +LL | / { +LL | | unit_fn_block(); +LL | | unit_fn_block() +LL | | }; + | |______^ + | +help: put the `;` here + | +LL ~ { +LL + unit_fn_block(); +LL + unit_fn_block(); +LL + } + | + +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:61:5 + | +LL | { m!(()) }; + | ^^^^^^^^^^^ help: put the `;` here: `{ m!(()); }` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/semicolon_outside_block.fixed b/tests/ui/semicolon_outside_block.fixed new file mode 100644 index 000000000000..5bc18faaad8e --- /dev/null +++ b/tests/ui/semicolon_outside_block.fixed @@ -0,0 +1,83 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_outside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()) }; + { m!(()) }; + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_outside_block.rs b/tests/ui/semicolon_outside_block.rs new file mode 100644 index 000000000000..0a4293238763 --- /dev/null +++ b/tests/ui/semicolon_outside_block.rs @@ -0,0 +1,83 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_outside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()) }; + { m!(()); } + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_outside_block.stderr b/tests/ui/semicolon_outside_block.stderr new file mode 100644 index 000000000000..fddf51208130 --- /dev/null +++ b/tests/ui/semicolon_outside_block.stderr @@ -0,0 +1,39 @@ +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:42:5 + | +LL | { unit_fn_block(); } + | ^^^^^^^^^^^^^^^^^^^^ help: put the `;` outside the block: `{ unit_fn_block() };` + | + = note: `-D clippy::semicolon-outside-block` implied by `-D warnings` + +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:43:5 + | +LL | unsafe { unit_fn_block(); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: put the `;` outside the block: `unsafe { unit_fn_block() };` + +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:52:5 + | +LL | / { +LL | | unit_fn_block(); +LL | | unit_fn_block(); +LL | | } + | |_____^ + | +help: put the `;` outside the block + | +LL ~ { +LL + unit_fn_block(); +LL + unit_fn_block() +LL + }; + | + +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:62:5 + | +LL | { m!(()); } + | ^^^^^^^^^^^ help: put the `;` outside the block: `{ m!(()) };` + +error: aborting due to 4 previous errors + From ba951e3ca72283d39757bd39d20bfd75ce901147 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 15 Nov 2022 18:29:43 +0100 Subject: [PATCH 266/524] Fix formatting of let chains --- clippy_lints/src/semicolon_block.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 911d38f65d6c..a41f89a6aa33 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -73,9 +73,9 @@ impl LateLintPass<'_> for SemicolonBlock { fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>) { if !block.span.from_expansion() - && let Some(tail) = block.expr - && let Some(block_expr @ Expr { kind: ExprKind::Block(_, _), ..}) = get_parent_expr_for_hir(cx, block.hir_id) - && let Some(Node::Stmt(Stmt { kind: StmtKind::Semi(_), span, .. })) = get_parent_node(cx.tcx, block_expr.hir_id) + && let Some(tail) = block.expr + && let Some(block_expr @ Expr { kind: ExprKind::Block(_, _), ..}) = get_parent_expr_for_hir(cx, block.hir_id) + && let Some(Node::Stmt(Stmt { kind: StmtKind::Semi(_), span, .. })) = get_parent_node(cx.tcx, block_expr.hir_id) { let expr_snip = snippet_with_macro_callsite(cx, tail.span, ".."); @@ -101,16 +101,18 @@ fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>) { fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>) { if !block.span.from_expansion() - && block.expr.is_none() - && let [.., Stmt { kind: StmtKind::Semi(expr), .. }] = block.stmts - && let Some(block_expr @ Expr { kind: ExprKind::Block(_, _), ..}) = get_parent_expr_for_hir(cx,block.hir_id) - && let Some(Node::Stmt(Stmt { kind: StmtKind::Expr(_), .. })) = get_parent_node(cx.tcx, block_expr.hir_id) { + && block.expr.is_none() + && let [.., Stmt { kind: StmtKind::Semi(expr), .. }] = block.stmts + && let Some(block_expr @ Expr { kind: ExprKind::Block(_, _), ..}) = get_parent_expr_for_hir(cx,block.hir_id) + && let Some(Node::Stmt(Stmt { kind: StmtKind::Expr(_), .. })) = get_parent_node(cx.tcx, block_expr.hir_id) + { let expr_snip = snippet_with_macro_callsite(cx, expr.span, ".."); let mut suggestion: String = snippet_with_macro_callsite(cx, block.span, "..").to_string(); if let Some((expr_offset, _)) = suggestion.rmatch_indices(&*expr_snip).next() - && let Some(semi_offset) = suggestion[expr_offset + expr_snip.len()..].find(';') { + && let Some(semi_offset) = suggestion[expr_offset + expr_snip.len()..].find(';') + { suggestion.remove(expr_offset + expr_snip.len() + semi_offset); } else { return; From 23744cd4ba57d2fc5c57459002282fe2d896193b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 15 Nov 2022 19:22:00 +0100 Subject: [PATCH 267/524] Use multi-span suggestions --- clippy_lints/src/semicolon_block.rs | 127 ++++++++++++------------ tests/ui/semicolon_inside_block.fixed | 4 +- tests/ui/semicolon_inside_block.stderr | 32 ++++-- tests/ui/semicolon_outside_block.fixed | 14 ++- tests/ui/semicolon_outside_block.stderr | 31 ++++-- 5 files changed, 124 insertions(+), 84 deletions(-) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index a41f89a6aa33..34bf97448273 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -1,10 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{get_parent_expr_for_hir, get_parent_node}; +use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, Node, Stmt, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -65,69 +64,73 @@ declare_clippy_lint! { declare_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]); impl LateLintPass<'_> for SemicolonBlock { - fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { - semicolon_inside_block(cx, block); - semicolon_outside_block(cx, block); - } -} - -fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>) { - if !block.span.from_expansion() - && let Some(tail) = block.expr - && let Some(block_expr @ Expr { kind: ExprKind::Block(_, _), ..}) = get_parent_expr_for_hir(cx, block.hir_id) - && let Some(Node::Stmt(Stmt { kind: StmtKind::Semi(_), span, .. })) = get_parent_node(cx.tcx, block_expr.hir_id) - { - let expr_snip = snippet_with_macro_callsite(cx, tail.span, ".."); - - let mut suggestion: String = snippet_with_macro_callsite(cx, block.span, "..").to_string(); - - if let Some((expr_offset, _)) = suggestion.rmatch_indices(&*expr_snip).next() { - suggestion.insert(expr_offset + expr_snip.len(), ';'); - } else { - return; + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + match stmt.kind { + StmtKind::Expr(Expr { + kind: + ExprKind::Block( + block @ Block { + expr: None, + stmts: + &[ + .., + Stmt { + kind: StmtKind::Semi(expr), + span, + .. + }, + ], + .. + }, + _, + ), + .. + }) if !block.span.from_expansion() => semicolon_outside_block(cx, block, expr, span), + StmtKind::Semi(Expr { + kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _), + .. + }) if !block.span.from_expansion() => semicolon_inside_block(cx, block, tail, stmt.span), + _ => (), } - - span_lint_and_sugg( - cx, - SEMICOLON_INSIDE_BLOCK, - *span, - "consider moving the `;` inside the block for consistent formatting", - "put the `;` here", - suggestion, - Applicability::MaybeIncorrect, - ); } } -fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>) { - if !block.span.from_expansion() - && block.expr.is_none() - && let [.., Stmt { kind: StmtKind::Semi(expr), .. }] = block.stmts - && let Some(block_expr @ Expr { kind: ExprKind::Block(_, _), ..}) = get_parent_expr_for_hir(cx,block.hir_id) - && let Some(Node::Stmt(Stmt { kind: StmtKind::Expr(_), .. })) = get_parent_node(cx.tcx, block_expr.hir_id) - { - let expr_snip = snippet_with_macro_callsite(cx, expr.span, ".."); - - let mut suggestion: String = snippet_with_macro_callsite(cx, block.span, "..").to_string(); +fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) { + let insert_span = tail.span.with_lo(tail.span.hi()); + let remove_span = semi_span.with_lo(block.span.hi()); - if let Some((expr_offset, _)) = suggestion.rmatch_indices(&*expr_snip).next() - && let Some(semi_offset) = suggestion[expr_offset + expr_snip.len()..].find(';') - { - suggestion.remove(expr_offset + expr_snip.len() + semi_offset); - } else { - return; - } + span_lint_and_then( + cx, + SEMICOLON_INSIDE_BLOCK, + semi_span, + "consider moving the `;` inside the block for consistent formatting", + |diag| { + multispan_sugg_with_applicability( + diag, + "put the `;` here", + Applicability::MachineApplicable, + [(remove_span, String::new()), (insert_span, ";".to_owned())], + ); + }, + ); +} - suggestion.push(';'); +fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>, semi_span: Span) { + let insert_span = block.span.with_lo(block.span.hi()); + let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi()); - span_lint_and_sugg( - cx, - SEMICOLON_OUTSIDE_BLOCK, - block.span, - "consider moving the `;` outside the block for consistent formatting", - "put the `;` outside the block", - suggestion, - Applicability::MaybeIncorrect, - ); - } + span_lint_and_then( + cx, + SEMICOLON_OUTSIDE_BLOCK, + block.span, + "consider moving the `;` outside the block for consistent formatting", + |diag| { + multispan_sugg_with_applicability( + diag, + "put the `;` here", + Applicability::MachineApplicable, + [(remove_span, String::new()), (insert_span, ";".to_owned())], + ); + }, + ); } diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed index 4cd112dd5e12..d2cd8b218829 100644 --- a/tests/ui/semicolon_inside_block.fixed +++ b/tests/ui/semicolon_inside_block.fixed @@ -10,7 +10,7 @@ macro_rules! m { (()) => { - () + (); }; (0) => {{ 0 @@ -58,7 +58,7 @@ fn main() { unit_fn_block(); }; - { m!(()); } + { m!(()) } { m!(()); } { m!(()); }; m!(0); diff --git a/tests/ui/semicolon_inside_block.stderr b/tests/ui/semicolon_inside_block.stderr index febe74b49bda..99f3b65be0f8 100644 --- a/tests/ui/semicolon_inside_block.stderr +++ b/tests/ui/semicolon_inside_block.stderr @@ -2,15 +2,26 @@ error: consider moving the `;` inside the block for consistent formatting --> $DIR/semicolon_inside_block.rs:39:5 | LL | { unit_fn_block() }; - | ^^^^^^^^^^^^^^^^^^^^ help: put the `;` here: `{ unit_fn_block(); }` + | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::semicolon-inside-block` implied by `-D warnings` +help: put the `;` here + | +LL - { unit_fn_block() }; +LL + { unit_fn_block(); } + | error: consider moving the `;` inside the block for consistent formatting --> $DIR/semicolon_inside_block.rs:40:5 | LL | unsafe { unit_fn_block() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: put the `;` here: `unsafe { unit_fn_block(); }` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: put the `;` here + | +LL - unsafe { unit_fn_block() }; +LL + unsafe { unit_fn_block(); } + | error: consider moving the `;` inside the block for consistent formatting --> $DIR/semicolon_inside_block.rs:48:5 @@ -23,17 +34,24 @@ LL | | }; | help: put the `;` here | -LL ~ { -LL + unit_fn_block(); -LL + unit_fn_block(); -LL + } +LL ~ unit_fn_block(); +LL ~ } | error: consider moving the `;` inside the block for consistent formatting --> $DIR/semicolon_inside_block.rs:61:5 | LL | { m!(()) }; - | ^^^^^^^^^^^ help: put the `;` here: `{ m!(()); }` + | ^^^^^^^^^^^ + | +help: put the `;` here + | +LL ~ (); +LL | }; + ... +LL | +LL ~ { m!(()) } + | error: aborting due to 4 previous errors diff --git a/tests/ui/semicolon_outside_block.fixed b/tests/ui/semicolon_outside_block.fixed index 5bc18faaad8e..35eacfe6d3f1 100644 --- a/tests/ui/semicolon_outside_block.fixed +++ b/tests/ui/semicolon_outside_block.fixed @@ -21,6 +21,9 @@ macro_rules! m { (2) => {{ 2; }}; + (stmt) => { + stmt; + }; } fn unit_fn_block() { @@ -39,8 +42,8 @@ fn main() { { unit_fn_block() }; unsafe { unit_fn_block() }; - { unit_fn_block() }; - unsafe { unit_fn_block() }; + { unit_fn_block(); } + unsafe { unit_fn_block(); } { unit_fn_block(); }; unsafe { unit_fn_block(); }; @@ -51,19 +54,20 @@ fn main() { }; { unit_fn_block(); - unit_fn_block() - }; + unit_fn_block(); + } { unit_fn_block(); unit_fn_block(); }; { m!(()) }; - { m!(()) }; + { m!(()); } { m!(()); }; m!(0); m!(1); m!(2); + { m!(stmt) }; for _ in [()] { unit_fn_block(); diff --git a/tests/ui/semicolon_outside_block.stderr b/tests/ui/semicolon_outside_block.stderr index fddf51208130..c2151f7af637 100644 --- a/tests/ui/semicolon_outside_block.stderr +++ b/tests/ui/semicolon_outside_block.stderr @@ -2,15 +2,26 @@ error: consider moving the `;` outside the block for consistent formatting --> $DIR/semicolon_outside_block.rs:42:5 | LL | { unit_fn_block(); } - | ^^^^^^^^^^^^^^^^^^^^ help: put the `;` outside the block: `{ unit_fn_block() };` + | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::semicolon-outside-block` implied by `-D warnings` +help: put the `;` here + | +LL - { unit_fn_block(); } +LL + { unit_fn_block() }; + | error: consider moving the `;` outside the block for consistent formatting --> $DIR/semicolon_outside_block.rs:43:5 | LL | unsafe { unit_fn_block(); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: put the `;` outside the block: `unsafe { unit_fn_block() };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: put the `;` here + | +LL - unsafe { unit_fn_block(); } +LL + unsafe { unit_fn_block() }; + | error: consider moving the `;` outside the block for consistent formatting --> $DIR/semicolon_outside_block.rs:52:5 @@ -21,19 +32,23 @@ LL | | unit_fn_block(); LL | | } | |_____^ | -help: put the `;` outside the block +help: put the `;` here | -LL ~ { -LL + unit_fn_block(); -LL + unit_fn_block() -LL + }; +LL ~ unit_fn_block() +LL ~ }; | error: consider moving the `;` outside the block for consistent formatting --> $DIR/semicolon_outside_block.rs:62:5 | LL | { m!(()); } - | ^^^^^^^^^^^ help: put the `;` outside the block: `{ m!(()) };` + | ^^^^^^^^^^^ + | +help: put the `;` here + | +LL - () +LL + (); }; + | error: aborting due to 4 previous errors From dd1163f4611d8597d03d2efd569d63a1d82bfd15 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 16 Nov 2022 14:30:53 +0100 Subject: [PATCH 268/524] Fix macro statement handling --- clippy_lints/src/semicolon_block.rs | 7 +++++-- tests/ui/semicolon_inside_block.fixed | 4 ++-- tests/ui/semicolon_inside_block.stderr | 7 ++----- tests/ui/semicolon_outside_block.fixed | 14 +++++--------- tests/ui/semicolon_outside_block.stderr | 4 ++-- 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 34bf97448273..57f835d45b12 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -96,7 +96,8 @@ impl LateLintPass<'_> for SemicolonBlock { } fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) { - let insert_span = tail.span.with_lo(tail.span.hi()); + let tail_span_end = tail.span.source_callsite().hi(); + let insert_span = Span::with_root_ctxt(tail_span_end, tail_span_end); let remove_span = semi_span.with_lo(block.span.hi()); span_lint_and_then( @@ -117,6 +118,8 @@ fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<' fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>, semi_span: Span) { let insert_span = block.span.with_lo(block.span.hi()); + // account for macro calls + let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span); let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi()); span_lint_and_then( diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed index d2cd8b218829..4cd112dd5e12 100644 --- a/tests/ui/semicolon_inside_block.fixed +++ b/tests/ui/semicolon_inside_block.fixed @@ -10,7 +10,7 @@ macro_rules! m { (()) => { - (); + () }; (0) => {{ 0 @@ -58,7 +58,7 @@ fn main() { unit_fn_block(); }; - { m!(()) } + { m!(()); } { m!(()); } { m!(()); }; m!(0); diff --git a/tests/ui/semicolon_inside_block.stderr b/tests/ui/semicolon_inside_block.stderr index 99f3b65be0f8..48d3690e2bde 100644 --- a/tests/ui/semicolon_inside_block.stderr +++ b/tests/ui/semicolon_inside_block.stderr @@ -46,11 +46,8 @@ LL | { m!(()) }; | help: put the `;` here | -LL ~ (); -LL | }; - ... -LL | -LL ~ { m!(()) } +LL - { m!(()) }; +LL + { m!(()); } | error: aborting due to 4 previous errors diff --git a/tests/ui/semicolon_outside_block.fixed b/tests/ui/semicolon_outside_block.fixed index 35eacfe6d3f1..5bc18faaad8e 100644 --- a/tests/ui/semicolon_outside_block.fixed +++ b/tests/ui/semicolon_outside_block.fixed @@ -21,9 +21,6 @@ macro_rules! m { (2) => {{ 2; }}; - (stmt) => { - stmt; - }; } fn unit_fn_block() { @@ -42,8 +39,8 @@ fn main() { { unit_fn_block() }; unsafe { unit_fn_block() }; - { unit_fn_block(); } - unsafe { unit_fn_block(); } + { unit_fn_block() }; + unsafe { unit_fn_block() }; { unit_fn_block(); }; unsafe { unit_fn_block(); }; @@ -54,20 +51,19 @@ fn main() { }; { unit_fn_block(); - unit_fn_block(); - } + unit_fn_block() + }; { unit_fn_block(); unit_fn_block(); }; { m!(()) }; - { m!(()); } + { m!(()) }; { m!(()); }; m!(0); m!(1); m!(2); - { m!(stmt) }; for _ in [()] { unit_fn_block(); diff --git a/tests/ui/semicolon_outside_block.stderr b/tests/ui/semicolon_outside_block.stderr index c2151f7af637..dcc102e60994 100644 --- a/tests/ui/semicolon_outside_block.stderr +++ b/tests/ui/semicolon_outside_block.stderr @@ -46,8 +46,8 @@ LL | { m!(()); } | help: put the `;` here | -LL - () -LL + (); }; +LL - { m!(()); } +LL + { m!(()) }; | error: aborting due to 4 previous errors From ab4154697512ec076c4c5550d218d46638353eb9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 24 Nov 2022 09:54:09 +0100 Subject: [PATCH 269/524] Address reviews --- clippy_lints/src/semicolon_block.rs | 35 +++++++++++++---------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 57f835d45b12..d06f21c89c4a 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -67,25 +67,21 @@ impl LateLintPass<'_> for SemicolonBlock { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { match stmt.kind { StmtKind::Expr(Expr { - kind: - ExprKind::Block( - block @ Block { - expr: None, - stmts: - &[ - .., - Stmt { - kind: StmtKind::Semi(expr), - span, - .. - }, - ], - .. - }, - _, - ), + kind: ExprKind::Block(block, _), .. - }) if !block.span.from_expansion() => semicolon_outside_block(cx, block, expr, span), + }) if !block.span.from_expansion() => { + let Block { + expr: None, + stmts: [.., stmt], + .. + } = block else { return }; + let &Stmt { + kind: StmtKind::Semi(expr), + span, + .. + } = stmt else { return }; + semicolon_outside_block(cx, block, expr, span) + }, StmtKind::Semi(Expr { kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _), .. @@ -96,8 +92,7 @@ impl LateLintPass<'_> for SemicolonBlock { } fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) { - let tail_span_end = tail.span.source_callsite().hi(); - let insert_span = Span::with_root_ctxt(tail_span_end, tail_span_end); + let insert_span = tail.span.source_callsite().shrink_to_hi(); let remove_span = semi_span.with_lo(block.span.hi()); span_lint_and_then( From f585414cd2fc635fecfe0437f9f71b08aba51d16 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 24 Nov 2022 20:37:07 +0100 Subject: [PATCH 270/524] Adjust semicolon block lint descriptions --- clippy_lints/src/semicolon_block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index d06f21c89c4a..6861b3709394 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -8,7 +8,7 @@ use rustc_span::Span; declare_clippy_lint! { /// ### What it does /// - /// For () returning expressions, check that the semicolon is inside the block. + /// Checks for semicolon terminated blocks containing only a single expression. /// /// ### Why is this bad? /// @@ -36,7 +36,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// - /// For () returning expressions, check that the semicolon is outside the block. + /// Checks for blocks containing only a single semicolon terminated statement. /// /// ### Why is this bad? /// From 459621d31be6f9b177372feae3a37a872a630f58 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 25 Nov 2022 21:50:38 +0100 Subject: [PATCH 271/524] Improve `EXIT` lint docs --- clippy_lints/src/exit.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 407dd1b39575..9c8b0d076dfd 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -7,21 +7,34 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// `exit()` terminates the program and doesn't provide a - /// stack trace. + /// Detects calls to the `exit()` function which terminates the program. /// /// ### Why is this bad? - /// Ideally a program is terminated by finishing + /// Exit terminates the program at the location it is called. For unrecoverable + /// errors `panics` should be used to provide a stacktrace and potentualy other + /// information. A normal termination or one with an error code should happen in /// the main function. /// /// ### Example - /// ```ignore + /// ``` /// std::process::exit(0) /// ``` + /// + /// Use instead: + /// + /// ```ignore + /// // To provide a stacktrace and additional information + /// panic!("message"); + /// + /// // or a main method with a return + /// fn main() -> Result<(), i32> { + /// Ok(()) + /// } + /// ``` #[clippy::version = "1.41.0"] pub EXIT, restriction, - "`std::process::exit` is called, terminating the program" + "detects `std::process::exit` calls" } declare_lint_pass!(Exit => [EXIT]); From 28976ce9c3e1358624c3fe1bd5fed80baa44bb6a Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sun, 20 Nov 2022 13:27:55 +0000 Subject: [PATCH 272/524] Link to a list of configurable lints in documentation --- README.md | 4 ++-- book/src/configuration.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f74de7de42b8..8c523152950c 100644 --- a/README.md +++ b/README.md @@ -197,8 +197,8 @@ disallowed-names = ["toto", "tata", "titi"] cognitive-complexity-threshold = 30 ``` -See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which -lints can be configured and the meaning of the variables. +See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), +the lint descriptions contain the names and meanings of these configuration variables. > **Note** > diff --git a/book/src/configuration.md b/book/src/configuration.md index 77f1d2e8797a..59d9ccd29009 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -11,8 +11,8 @@ disallowed-names = ["toto", "tata", "titi"] cognitive-complexity-threshold = 30 ``` -See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which -lints can be configured and the meaning of the variables. +See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), +the lint descriptions contain the names and meanings of these configuration variables. To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. From 461e219d1d4167139a842b290cd1c019ff2d52a2 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 19 Nov 2022 12:50:02 +0000 Subject: [PATCH 273/524] Allow using `clippy::msrv` as an outer attribute --- README.md | 2 +- book/src/configuration.md | 2 +- book/src/development/adding_lints.md | 10 +- clippy_utils/src/attrs.rs | 24 +-- clippy_utils/src/msrvs.rs | 4 +- tests/ui/almost_complete_letter_range.fixed | 5 +- tests/ui/almost_complete_letter_range.rs | 5 +- tests/ui/almost_complete_letter_range.stderr | 26 +-- tests/ui/cast_abs_to_unsigned.fixed | 7 +- tests/ui/cast_abs_to_unsigned.rs | 7 +- tests/ui/cast_abs_to_unsigned.stderr | 36 ++-- tests/ui/cast_lossless_bool.fixed | 7 +- tests/ui/cast_lossless_bool.rs | 7 +- tests/ui/cast_lossless_bool.stderr | 28 +-- tests/ui/cfg_attr_rustfmt.fixed | 8 +- tests/ui/cfg_attr_rustfmt.rs | 8 +- tests/ui/cfg_attr_rustfmt.stderr | 2 +- tests/ui/checked_conversions.fixed | 7 +- tests/ui/checked_conversions.rs | 7 +- tests/ui/checked_conversions.stderr | 34 ++-- tests/ui/cloned_instead_of_copied.fixed | 10 +- tests/ui/cloned_instead_of_copied.rs | 10 +- tests/ui/cloned_instead_of_copied.stderr | 16 +- tests/ui/err_expect.fixed | 7 +- tests/ui/err_expect.rs | 7 +- tests/ui/err_expect.stderr | 4 +- tests/ui/filter_map_next_fixable.fixed | 7 +- tests/ui/filter_map_next_fixable.rs | 7 +- tests/ui/filter_map_next_fixable.stderr | 4 +- tests/ui/from_over_into.fixed | 7 +- tests/ui/from_over_into.rs | 7 +- tests/ui/from_over_into.stderr | 10 +- tests/ui/if_then_some_else_none.rs | 5 +- tests/ui/if_then_some_else_none.stderr | 10 +- tests/ui/manual_clamp.rs | 7 +- tests/ui/manual_clamp.stderr | 70 ++++---- tests/ui/manual_is_ascii_check.fixed | 11 +- tests/ui/manual_is_ascii_check.rs | 11 +- tests/ui/manual_is_ascii_check.stderr | 22 +-- tests/ui/manual_rem_euclid.fixed | 13 +- tests/ui/manual_rem_euclid.rs | 13 +- tests/ui/manual_rem_euclid.stderr | 20 +-- tests/ui/manual_retain.fixed | 7 +- tests/ui/manual_retain.rs | 7 +- tests/ui/manual_retain.stderr | 38 ++-- tests/ui/manual_split_once.fixed | 5 +- tests/ui/manual_split_once.rs | 5 +- tests/ui/manual_split_once.stderr | 38 ++-- tests/ui/manual_str_repeat.fixed | 5 +- tests/ui/manual_str_repeat.rs | 5 +- tests/ui/manual_str_repeat.stderr | 20 +-- tests/ui/manual_strip.rs | 7 +- tests/ui/manual_strip.stderr | 32 ++-- tests/ui/map_unwrap_or.rs | 7 +- tests/ui/map_unwrap_or.stderr | 24 +-- tests/ui/match_expr_like_matches_macro.fixed | 7 +- tests/ui/match_expr_like_matches_macro.rs | 7 +- tests/ui/match_expr_like_matches_macro.stderr | 28 +-- tests/ui/mem_replace.fixed | 7 +- tests/ui/mem_replace.rs | 7 +- tests/ui/mem_replace.stderr | 40 ++--- tests/ui/min_rust_version_attr.rs | 18 +- tests/ui/min_rust_version_attr.stderr | 12 +- tests/ui/min_rust_version_invalid_attr.stderr | 2 +- .../ui/missing_const_for_fn/cant_be_const.rs | 4 +- .../ui/missing_const_for_fn/could_be_const.rs | 10 +- .../could_be_const.stderr | 24 ++- tests/ui/needless_borrow.fixed | 33 +--- tests/ui/needless_borrow.rs | 33 +--- tests/ui/needless_borrow.stderr | 18 +- tests/ui/needless_question_mark.fixed | 1 - tests/ui/needless_question_mark.rs | 1 - tests/ui/needless_question_mark.stderr | 28 +-- tests/ui/needless_splitn.fixed | 3 +- tests/ui/needless_splitn.rs | 3 +- tests/ui/needless_splitn.stderr | 26 +-- tests/ui/option_as_ref_deref.fixed | 7 +- tests/ui/option_as_ref_deref.rs | 7 +- tests/ui/option_as_ref_deref.stderr | 36 ++-- tests/ui/ptr_as_ptr.fixed | 5 +- tests/ui/ptr_as_ptr.rs | 5 +- tests/ui/ptr_as_ptr.stderr | 16 +- tests/ui/range_contains.fixed | 7 +- tests/ui/range_contains.rs | 7 +- tests/ui/range_contains.stderr | 42 ++--- tests/ui/redundant_field_names.fixed | 7 +- tests/ui/redundant_field_names.rs | 7 +- tests/ui/redundant_field_names.stderr | 16 +- tests/ui/redundant_static_lifetimes.fixed | 7 +- tests/ui/redundant_static_lifetimes.rs | 7 +- tests/ui/redundant_static_lifetimes.stderr | 34 ++-- tests/ui/seek_from_current.fixed | 5 +- tests/ui/seek_from_current.rs | 5 +- tests/ui/seek_from_current.stderr | 2 +- .../ui/seek_to_start_instead_of_rewind.fixed | 7 +- tests/ui/seek_to_start_instead_of_rewind.rs | 7 +- .../ui/seek_to_start_instead_of_rewind.stderr | 6 +- tests/ui/transmute_ptr_to_ref.fixed | 5 +- tests/ui/transmute_ptr_to_ref.rs | 5 +- tests/ui/transmute_ptr_to_ref.stderr | 44 ++--- tests/ui/uninlined_format_args.fixed | 5 +- tests/ui/uninlined_format_args.rs | 5 +- tests/ui/uninlined_format_args.stderr | 152 ++++++++-------- tests/ui/unnecessary_to_owned.fixed | 5 +- tests/ui/unnecessary_to_owned.rs | 5 +- tests/ui/unnecessary_to_owned.stderr | 168 +++++++++--------- tests/ui/unnested_or_patterns.fixed | 8 +- tests/ui/unnested_or_patterns.rs | 8 +- tests/ui/unnested_or_patterns.stderr | 2 +- tests/ui/use_self.fixed | 7 +- tests/ui/use_self.rs | 7 +- tests/ui/use_self.stderr | 84 ++++----- 112 files changed, 799 insertions(+), 979 deletions(-) diff --git a/README.md b/README.md index f74de7de42b8..1c199d57f514 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ in the `Cargo.toml` can be used. rust-version = "1.30" ``` -The MSRV can also be specified as an inner attribute, like below. +The MSRV can also be specified as an attribute, like below. ```rust #![feature(custom_inner_attributes)] diff --git a/book/src/configuration.md b/book/src/configuration.md index 77f1d2e8797a..5419eb5c21dc 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -72,7 +72,7 @@ minimum supported Rust version (MSRV) in the clippy configuration file. msrv = "1.30.0" ``` -The MSRV can also be specified as an inner attribute, like below. +The MSRV can also be specified as an attribute, like below. ```rust #![feature(custom_inner_attributes)] diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 29d0d45dcc96..8b4eee8c9d94 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -463,7 +463,7 @@ if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) { } ``` -The project's MSRV can also be specified as an inner attribute, which overrides +The project's MSRV can also be specified as an attribute, which overrides the value from `clippy.toml`. This can be accounted for using the `extract_msrv_attr!(LintContext)` macro and passing `LateContext`/`EarlyContext`. @@ -483,19 +483,15 @@ have a case for the version below the MSRV and one with the same contents but for the MSRV version itself. ```rust -#![feature(custom_inner_attributes)] - ... +#[clippy::msrv = "1.44"] fn msrv_1_44() { - #![clippy::msrv = "1.44"] - /* something that would trigger the lint */ } +#[clippy::msrv = "1.45"] fn msrv_1_45() { - #![clippy::msrv = "1.45"] - /* something that would trigger the lint */ } ``` diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index cd8575c90e86..7987a233bdc1 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -125,19 +125,19 @@ fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &' } } -pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option { - let mut unique_attr = None; +pub fn get_unique_attr<'a>( + sess: &'a Session, + attrs: &'a [ast::Attribute], + name: &'static str, +) -> Option<&'a ast::Attribute> { + let mut unique_attr: Option<&ast::Attribute> = None; for attr in get_attr(sess, attrs, name) { - match attr.style { - ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), - ast::AttrStyle::Inner => { - sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times")) - .span_note(unique_attr.as_ref().unwrap().span, "first definition found here") - .emit(); - }, - ast::AttrStyle::Outer => { - sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute")); - }, + if let Some(duplicate) = unique_attr { + sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times")) + .span_note(duplicate.span, "first definition found here") + .emit(); + } else { + unique_attr = Some(attr); } } unique_attr diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 2c9f75736e52..12a512f78a69 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -5,7 +5,7 @@ use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::Span; -use crate::attrs::get_unique_inner_attr; +use crate::attrs::get_unique_attr; macro_rules! msrv_aliases { ($($major:literal,$minor:literal,$patch:literal { @@ -118,7 +118,7 @@ impl Msrv { } fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option { - if let Some(msrv_attr) = get_unique_inner_attr(sess, attrs, "msrv") { + if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") { if let Some(msrv) = msrv_attr.value_str() { return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span)); } diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed index 079b7c000dce..adcbd4d5134d 100644 --- a/tests/ui/almost_complete_letter_range.fixed +++ b/tests/ui/almost_complete_letter_range.fixed @@ -2,7 +2,6 @@ // edition:2018 // aux-build:macro_rules.rs -#![feature(custom_inner_attributes)] #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] @@ -62,16 +61,16 @@ fn main() { b!(); } +#[clippy::msrv = "1.25"] fn _under_msrv() { - #![clippy::msrv = "1.25"] let _ = match 'a' { 'a'...'z' => 1, _ => 2, }; } +#[clippy::msrv = "1.26"] fn _meets_msrv() { - #![clippy::msrv = "1.26"] let _ = 'a'..='z'; let _ = match 'a' { 'a'..='z' => 1, diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs index a66900a976ef..9979316eca42 100644 --- a/tests/ui/almost_complete_letter_range.rs +++ b/tests/ui/almost_complete_letter_range.rs @@ -2,7 +2,6 @@ // edition:2018 // aux-build:macro_rules.rs -#![feature(custom_inner_attributes)] #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] @@ -62,16 +61,16 @@ fn main() { b!(); } +#[clippy::msrv = "1.25"] fn _under_msrv() { - #![clippy::msrv = "1.25"] let _ = match 'a' { 'a'..'z' => 1, _ => 2, }; } +#[clippy::msrv = "1.26"] fn _meets_msrv() { - #![clippy::msrv = "1.26"] let _ = 'a'..'z'; let _ = match 'a' { 'a'..'z' => 1, diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr index 3de44c72c1b9..9abf6d6c5e7d 100644 --- a/tests/ui/almost_complete_letter_range.stderr +++ b/tests/ui/almost_complete_letter_range.stderr @@ -1,5 +1,5 @@ error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:30:17 + --> $DIR/almost_complete_letter_range.rs:29:17 | LL | let _ = ('a') ..'z'; | ^^^^^^--^^^ @@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z'; = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:31:17 + --> $DIR/almost_complete_letter_range.rs:30:17 | LL | let _ = 'A' .. ('Z'); | ^^^^--^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z'); | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:37:13 + --> $DIR/almost_complete_letter_range.rs:36:13 | LL | let _ = (b'a')..(b'z'); | ^^^^^^--^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = (b'a')..(b'z'); | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:38:13 + --> $DIR/almost_complete_letter_range.rs:37:13 | LL | let _ = b'A'..b'Z'; | ^^^^--^^^^ @@ -33,7 +33,7 @@ LL | let _ = b'A'..b'Z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:43:13 + --> $DIR/almost_complete_letter_range.rs:42:13 | LL | let _ = a!()..'z'; | ^^^^--^^^ @@ -41,7 +41,7 @@ LL | let _ = a!()..'z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:46:9 + --> $DIR/almost_complete_letter_range.rs:45:9 | LL | b'a'..b'z' if true => 1, | ^^^^--^^^^ @@ -49,7 +49,7 @@ LL | b'a'..b'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:47:9 + --> $DIR/almost_complete_letter_range.rs:46:9 | LL | b'A'..b'Z' if true => 2, | ^^^^--^^^^ @@ -57,7 +57,7 @@ LL | b'A'..b'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:54:9 + --> $DIR/almost_complete_letter_range.rs:53:9 | LL | 'a'..'z' if true => 1, | ^^^--^^^ @@ -65,7 +65,7 @@ LL | 'a'..'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:55:9 + --> $DIR/almost_complete_letter_range.rs:54:9 | LL | 'A'..'Z' if true => 2, | ^^^--^^^ @@ -73,7 +73,7 @@ LL | 'A'..'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:23:17 + --> $DIR/almost_complete_letter_range.rs:22:17 | LL | let _ = 'a'..'z'; | ^^^--^^^ @@ -86,7 +86,7 @@ LL | b!(); = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:68:9 + --> $DIR/almost_complete_letter_range.rs:67:9 | LL | 'a'..'z' => 1, | ^^^--^^^ @@ -94,7 +94,7 @@ LL | 'a'..'z' => 1, | help: use an inclusive range: `...` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:75:13 + --> $DIR/almost_complete_letter_range.rs:74:13 | LL | let _ = 'a'..'z'; | ^^^--^^^ @@ -102,7 +102,7 @@ LL | let _ = 'a'..'z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:77:9 + --> $DIR/almost_complete_letter_range.rs:76:9 | LL | 'a'..'z' => 1, | ^^^--^^^ diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index e6bf944c7a5e..8676b562b4f9 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] #![allow(clippy::uninlined_format_args, unused)] @@ -33,16 +32,14 @@ fn main() { let _ = (x as i64 - y as i64).unsigned_abs() as u32; } +#[clippy::msrv = "1.50"] fn msrv_1_50() { - #![clippy::msrv = "1.50"] - let x: i32 = 10; assert_eq!(10u32, x.abs() as u32); } +#[clippy::msrv = "1.51"] fn msrv_1_51() { - #![clippy::msrv = "1.51"] - let x: i32 = 10; assert_eq!(10u32, x.unsigned_abs()); } diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index c87320b5209d..5775af874f8f 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] #![allow(clippy::uninlined_format_args, unused)] @@ -33,16 +32,14 @@ fn main() { let _ = (x as i64 - y as i64).abs() as u32; } +#[clippy::msrv = "1.50"] fn msrv_1_50() { - #![clippy::msrv = "1.50"] - let x: i32 = 10; assert_eq!(10u32, x.abs() as u32); } +#[clippy::msrv = "1.51"] fn msrv_1_51() { - #![clippy::msrv = "1.51"] - let x: i32 = 10; assert_eq!(10u32, x.abs() as u32); } diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 1b39c554b038..4668554f4511 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:9:18 + --> $DIR/cast_abs_to_unsigned.rs:8:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,103 +7,103 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:13:20 + --> $DIR/cast_abs_to_unsigned.rs:12:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:14:20 + --> $DIR/cast_abs_to_unsigned.rs:13:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:15:13 + --> $DIR/cast_abs_to_unsigned.rs:14:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:17:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:21:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:22:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:23:13 + --> $DIR/cast_abs_to_unsigned.rs:22:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:25:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:29:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:30:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:31:13 + --> $DIR/cast_abs_to_unsigned.rs:30:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:33:13 + --> $DIR/cast_abs_to_unsigned.rs:32:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:47:23 + --> $DIR/cast_abs_to_unsigned.rs:44:23 | LL | assert_eq!(10u32, x.abs() as u32); | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` diff --git a/tests/ui/cast_lossless_bool.fixed b/tests/ui/cast_lossless_bool.fixed index af13b755e310..13b3cf838c9f 100644 --- a/tests/ui/cast_lossless_bool.fixed +++ b/tests/ui/cast_lossless_bool.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -42,14 +41,12 @@ mod cast_lossless_in_impl { } } +#[clippy::msrv = "1.27"] fn msrv_1_27() { - #![clippy::msrv = "1.27"] - let _ = true as u8; } +#[clippy::msrv = "1.28"] fn msrv_1_28() { - #![clippy::msrv = "1.28"] - let _ = u8::from(true); } diff --git a/tests/ui/cast_lossless_bool.rs b/tests/ui/cast_lossless_bool.rs index 3b06af899c60..3eed2135562c 100644 --- a/tests/ui/cast_lossless_bool.rs +++ b/tests/ui/cast_lossless_bool.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -42,14 +41,12 @@ mod cast_lossless_in_impl { } } +#[clippy::msrv = "1.27"] fn msrv_1_27() { - #![clippy::msrv = "1.27"] - let _ = true as u8; } +#[clippy::msrv = "1.28"] fn msrv_1_28() { - #![clippy::msrv = "1.28"] - let _ = true as u8; } diff --git a/tests/ui/cast_lossless_bool.stderr b/tests/ui/cast_lossless_bool.stderr index 768b033d10a2..ce240b70f891 100644 --- a/tests/ui/cast_lossless_bool.stderr +++ b/tests/ui/cast_lossless_bool.stderr @@ -1,5 +1,5 @@ error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> $DIR/cast_lossless_bool.rs:9:13 + --> $DIR/cast_lossless_bool.rs:8:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` @@ -7,79 +7,79 @@ LL | let _ = true as u8; = note: `-D clippy::cast-lossless` implied by `-D warnings` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:10:13 + --> $DIR/cast_lossless_bool.rs:9:13 | LL | let _ = true as u16; | ^^^^^^^^^^^ help: try: `u16::from(true)` error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)` - --> $DIR/cast_lossless_bool.rs:11:13 + --> $DIR/cast_lossless_bool.rs:10:13 | LL | let _ = true as u32; | ^^^^^^^^^^^ help: try: `u32::from(true)` error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)` - --> $DIR/cast_lossless_bool.rs:12:13 + --> $DIR/cast_lossless_bool.rs:11:13 | LL | let _ = true as u64; | ^^^^^^^^^^^ help: try: `u64::from(true)` error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)` - --> $DIR/cast_lossless_bool.rs:13:13 + --> $DIR/cast_lossless_bool.rs:12:13 | LL | let _ = true as u128; | ^^^^^^^^^^^^ help: try: `u128::from(true)` error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)` - --> $DIR/cast_lossless_bool.rs:14:13 + --> $DIR/cast_lossless_bool.rs:13:13 | LL | let _ = true as usize; | ^^^^^^^^^^^^^ help: try: `usize::from(true)` error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)` - --> $DIR/cast_lossless_bool.rs:16:13 + --> $DIR/cast_lossless_bool.rs:15:13 | LL | let _ = true as i8; | ^^^^^^^^^^ help: try: `i8::from(true)` error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)` - --> $DIR/cast_lossless_bool.rs:17:13 + --> $DIR/cast_lossless_bool.rs:16:13 | LL | let _ = true as i16; | ^^^^^^^^^^^ help: try: `i16::from(true)` error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)` - --> $DIR/cast_lossless_bool.rs:18:13 + --> $DIR/cast_lossless_bool.rs:17:13 | LL | let _ = true as i32; | ^^^^^^^^^^^ help: try: `i32::from(true)` error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)` - --> $DIR/cast_lossless_bool.rs:19:13 + --> $DIR/cast_lossless_bool.rs:18:13 | LL | let _ = true as i64; | ^^^^^^^^^^^ help: try: `i64::from(true)` error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)` - --> $DIR/cast_lossless_bool.rs:20:13 + --> $DIR/cast_lossless_bool.rs:19:13 | LL | let _ = true as i128; | ^^^^^^^^^^^^ help: try: `i128::from(true)` error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)` - --> $DIR/cast_lossless_bool.rs:21:13 + --> $DIR/cast_lossless_bool.rs:20:13 | LL | let _ = true as isize; | ^^^^^^^^^^^^^ help: try: `isize::from(true)` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:24:13 + --> $DIR/cast_lossless_bool.rs:23:13 | LL | let _ = (true | false) as u16; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> $DIR/cast_lossless_bool.rs:54:13 + --> $DIR/cast_lossless_bool.rs:51:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` diff --git a/tests/ui/cfg_attr_rustfmt.fixed b/tests/ui/cfg_attr_rustfmt.fixed index 8a5645b22ed1..b970b1209b65 100644 --- a/tests/ui/cfg_attr_rustfmt.fixed +++ b/tests/ui/cfg_attr_rustfmt.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes, custom_inner_attributes)] +#![feature(stmt_expr_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -30,16 +30,14 @@ mod foo { pub fn f() {} } +#[clippy::msrv = "1.29"] fn msrv_1_29() { - #![clippy::msrv = "1.29"] - #[cfg_attr(rustfmt, rustfmt::skip)] 1+29; } +#[clippy::msrv = "1.30"] fn msrv_1_30() { - #![clippy::msrv = "1.30"] - #[rustfmt::skip] 1+30; } diff --git a/tests/ui/cfg_attr_rustfmt.rs b/tests/ui/cfg_attr_rustfmt.rs index 2fb140efae76..0a8e6a89d8a0 100644 --- a/tests/ui/cfg_attr_rustfmt.rs +++ b/tests/ui/cfg_attr_rustfmt.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes, custom_inner_attributes)] +#![feature(stmt_expr_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -30,16 +30,14 @@ mod foo { pub fn f() {} } +#[clippy::msrv = "1.29"] fn msrv_1_29() { - #![clippy::msrv = "1.29"] - #[cfg_attr(rustfmt, rustfmt::skip)] 1+29; } +#[clippy::msrv = "1.30"] fn msrv_1_30() { - #![clippy::msrv = "1.30"] - #[cfg_attr(rustfmt, rustfmt::skip)] 1+30; } diff --git a/tests/ui/cfg_attr_rustfmt.stderr b/tests/ui/cfg_attr_rustfmt.stderr index 08df7b2b39a0..524a2bf72484 100644 --- a/tests/ui/cfg_attr_rustfmt.stderr +++ b/tests/ui/cfg_attr_rustfmt.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(rustfmt, rustfmt_skip)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes - --> $DIR/cfg_attr_rustfmt.rs:43:5 + --> $DIR/cfg_attr_rustfmt.rs:41:5 | LL | #[cfg_attr(rustfmt, rustfmt::skip)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index f936957cb40c..e279ba3147bb 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, unused, @@ -78,16 +77,14 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +#[clippy::msrv = "1.33"] fn msrv_1_33() { - #![clippy::msrv = "1.33"] - let value: i64 = 33; let _ = value <= (u32::MAX as i64) && value >= 0; } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let value: i64 = 34; let _ = u32::try_from(value).is_ok(); } diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index 77aec713ff31..9d7a40995c37 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, unused, @@ -78,16 +77,14 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +#[clippy::msrv = "1.33"] fn msrv_1_33() { - #![clippy::msrv = "1.33"] - let value: i64 = 33; let _ = value <= (u32::MAX as i64) && value >= 0; } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let value: i64 = 34; let _ = value <= (u32::MAX as i64) && value >= 0; } diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index b2bf7af8daf8..273ead73bda5 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:17:13 + --> $DIR/checked_conversions.rs:16:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,97 +7,97 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = note: `-D clippy::checked-conversions` implied by `-D warnings` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:18:13 + --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:22:13 + --> $DIR/checked_conversions.rs:21:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:23:13 + --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:27:13 + --> $DIR/checked_conversions.rs:26:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:28:13 + --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:34:13 + --> $DIR/checked_conversions.rs:33:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:35:13 + --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:39:13 + --> $DIR/checked_conversions.rs:38:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:40:13 + --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:46:13 + --> $DIR/checked_conversions.rs:45:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:47:13 + --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:51:13 + --> $DIR/checked_conversions.rs:50:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:52:13 + --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:56:13 + --> $DIR/checked_conversions.rs:55:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:57:13 + --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:92:13 + --> $DIR/checked_conversions.rs:89:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed index 42ed232d1001..ecbfc1feedf6 100644 --- a/tests/ui/cloned_instead_of_copied.fixed +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] #![allow(unused)] @@ -17,23 +16,20 @@ fn main() { let _ = Some(&String::new()).cloned(); } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let _ = [1].iter().cloned(); let _ = Some(&1).cloned(); } +#[clippy::msrv = "1.35"] fn msrv_1_35() { - #![clippy::msrv = "1.35"] - let _ = [1].iter().cloned(); let _ = Some(&1).copied(); // Option::copied needs 1.35 } +#[clippy::msrv = "1.36"] fn msrv_1_36() { - #![clippy::msrv = "1.36"] - let _ = [1].iter().copied(); // Iterator::copied needs 1.36 let _ = Some(&1).copied(); } diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs index 471bd9654cc1..163dc3dddce6 100644 --- a/tests/ui/cloned_instead_of_copied.rs +++ b/tests/ui/cloned_instead_of_copied.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] #![allow(unused)] @@ -17,23 +16,20 @@ fn main() { let _ = Some(&String::new()).cloned(); } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let _ = [1].iter().cloned(); let _ = Some(&1).cloned(); } +#[clippy::msrv = "1.35"] fn msrv_1_35() { - #![clippy::msrv = "1.35"] - let _ = [1].iter().cloned(); let _ = Some(&1).cloned(); // Option::copied needs 1.35 } +#[clippy::msrv = "1.36"] fn msrv_1_36() { - #![clippy::msrv = "1.36"] - let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 let _ = Some(&1).cloned(); } diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr index 914c9a91e830..e0361acd9560 100644 --- a/tests/ui/cloned_instead_of_copied.stderr +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -1,5 +1,5 @@ error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:9:24 + --> $DIR/cloned_instead_of_copied.rs:8:24 | LL | let _ = [1].iter().cloned(); | ^^^^^^ help: try: `copied` @@ -7,43 +7,43 @@ LL | let _ = [1].iter().cloned(); = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:10:31 + --> $DIR/cloned_instead_of_copied.rs:9:31 | LL | let _ = vec!["hi"].iter().cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:11:22 + --> $DIR/cloned_instead_of_copied.rs:10:22 | LL | let _ = Some(&1).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:12:34 + --> $DIR/cloned_instead_of_copied.rs:11:34 | LL | let _ = Box::new([1].iter()).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:13:32 + --> $DIR/cloned_instead_of_copied.rs:12:32 | LL | let _ = Box::new(Some(&1)).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:31:22 + --> $DIR/cloned_instead_of_copied.rs:28:22 | LL | let _ = Some(&1).cloned(); // Option::copied needs 1.35 | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:37:24 + --> $DIR/cloned_instead_of_copied.rs:33:24 | LL | let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:38:22 + --> $DIR/cloned_instead_of_copied.rs:34:22 | LL | let _ = Some(&1).cloned(); | ^^^^^^ help: try: `copied` diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed index 3bac738acd65..b63cbd8a8e6b 100644 --- a/tests/ui/err_expect.fixed +++ b/tests/ui/err_expect.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] struct MyTypeNonDebug; @@ -16,16 +15,14 @@ fn main() { test_non_debug.err().expect("Testing non debug type"); } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - let x: Result = Ok(16); x.err().expect("16"); } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - let x: Result = Ok(17); x.expect_err("17"); } diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs index 6e7c47d9ad3c..c081a745fb40 100644 --- a/tests/ui/err_expect.rs +++ b/tests/ui/err_expect.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] struct MyTypeNonDebug; @@ -16,16 +15,14 @@ fn main() { test_non_debug.err().expect("Testing non debug type"); } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - let x: Result = Ok(16); x.err().expect("16"); } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - let x: Result = Ok(17); x.err().expect("17"); } diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr index 91a6cf8de65f..82c0754cfb00 100644 --- a/tests/ui/err_expect.stderr +++ b/tests/ui/err_expect.stderr @@ -1,5 +1,5 @@ error: called `.err().expect()` on a `Result` value - --> $DIR/err_expect.rs:13:16 + --> $DIR/err_expect.rs:12:16 | LL | test_debug.err().expect("Testing debug type"); | ^^^^^^^^^^^^ help: try: `expect_err` @@ -7,7 +7,7 @@ LL | test_debug.err().expect("Testing debug type"); = note: `-D clippy::err-expect` implied by `-D warnings` error: called `.err().expect()` on a `Result` value - --> $DIR/err_expect.rs:30:7 + --> $DIR/err_expect.rs:27:7 | LL | x.err().expect("17"); | ^^^^^^^^^^^^ help: try: `expect_err` diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed index 41828ddd7acd..462d46169fcb 100644 --- a/tests/ui/filter_map_next_fixable.fixed +++ b/tests/ui/filter_map_next_fixable.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] #![allow(unused)] @@ -11,16 +10,14 @@ fn main() { assert_eq!(element, Some(1)); } +#[clippy::msrv = "1.29"] fn msrv_1_29() { - #![clippy::msrv = "1.29"] - let a = ["1", "lol", "3", "NaN", "5"]; let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); } +#[clippy::msrv = "1.30"] fn msrv_1_30() { - #![clippy::msrv = "1.30"] - let a = ["1", "lol", "3", "NaN", "5"]; let _: Option = a.iter().find_map(|s| s.parse().ok()); } diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs index be492a81b45e..2ea00cf73072 100644 --- a/tests/ui/filter_map_next_fixable.rs +++ b/tests/ui/filter_map_next_fixable.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] #![allow(unused)] @@ -11,16 +10,14 @@ fn main() { assert_eq!(element, Some(1)); } +#[clippy::msrv = "1.29"] fn msrv_1_29() { - #![clippy::msrv = "1.29"] - let a = ["1", "lol", "3", "NaN", "5"]; let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); } +#[clippy::msrv = "1.30"] fn msrv_1_30() { - #![clippy::msrv = "1.30"] - let a = ["1", "lol", "3", "NaN", "5"]; let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); } diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr index e789efeabd55..a9fc6abe88fa 100644 --- a/tests/ui/filter_map_next_fixable.stderr +++ b/tests/ui/filter_map_next_fixable.stderr @@ -1,5 +1,5 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> $DIR/filter_map_next_fixable.rs:10:32 + --> $DIR/filter_map_next_fixable.rs:9:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` @@ -7,7 +7,7 @@ LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next = note: `-D clippy::filter-map-next` implied by `-D warnings` error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> $DIR/filter_map_next_fixable.rs:25:26 + --> $DIR/filter_map_next_fixable.rs:22:26 | LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed index 1cf49ca45f49..125c9a69cd3f 100644 --- a/tests/ui/from_over_into.fixed +++ b/tests/ui/from_over_into.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -60,9 +59,8 @@ impl From for A { } } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - struct FromOverInto(Vec); impl Into> for Vec { @@ -72,9 +70,8 @@ fn msrv_1_40() { } } +#[clippy::msrv = "1.41"] fn msrv_1_41() { - #![clippy::msrv = "1.41"] - struct FromOverInto(Vec); impl From> for FromOverInto { diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index d30f3c3fc925..5aa127bfabe4 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -60,9 +59,8 @@ impl From for A { } } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - struct FromOverInto(Vec); impl Into> for Vec { @@ -72,9 +70,8 @@ fn msrv_1_40() { } } +#[clippy::msrv = "1.41"] fn msrv_1_41() { - #![clippy::msrv = "1.41"] - struct FromOverInto(Vec); impl Into> for Vec { diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 9c2a7c04c364..a1764a5ea12a 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,5 +1,5 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:10:1 + --> $DIR/from_over_into.rs:9:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL ~ StringWrapper(val) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:18:1 + --> $DIR/from_over_into.rs:17:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL ~ SelfType(String::new()) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:33:1 + --> $DIR/from_over_into.rs:32:1 | LL | impl Into for X { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL ~ let _: X = val; | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:45:1 + --> $DIR/from_over_into.rs:44:1 | LL | impl core::convert::Into for crate::ExplicitPaths { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ LL ~ val.0 | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:80:5 + --> $DIR/from_over_into.rs:77:5 | LL | impl Into> for Vec { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 3bc3a0395244..0e89fdb0dfa2 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -1,5 +1,4 @@ #![warn(clippy::if_then_some_else_none)] -#![feature(custom_inner_attributes)] fn main() { // Should issue an error. @@ -66,8 +65,8 @@ fn main() { let _ = if foo() { into_some("foo") } else { None }; } +#[clippy::msrv = "1.49"] fn _msrv_1_49() { - #![clippy::msrv = "1.49"] // `bool::then` was stabilized in 1.50. Do not lint this let _ = if foo() { println!("true!"); @@ -77,8 +76,8 @@ fn _msrv_1_49() { }; } +#[clippy::msrv = "1.50"] fn _msrv_1_50() { - #![clippy::msrv = "1.50"] let _ = if foo() { println!("true!"); Some(150) diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index 24e0b5947f19..d728a3c31a3b 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -1,5 +1,5 @@ error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:6:13 + --> $DIR/if_then_some_else_none.rs:5:13 | LL | let _ = if foo() { | _____________^ @@ -14,7 +14,7 @@ LL | | }; = note: `-D clippy::if-then-some-else-none` implied by `-D warnings` error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:14:13 + --> $DIR/if_then_some_else_none.rs:13:13 | LL | let _ = if matches!(true, true) { | _____________^ @@ -28,7 +28,7 @@ LL | | }; = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })` error: this could be simplified with `bool::then_some` - --> $DIR/if_then_some_else_none.rs:23:28 + --> $DIR/if_then_some_else_none.rs:22:28 | LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); = help: consider using `bool::then_some` like: `(o < 32).then_some(o)` error: this could be simplified with `bool::then_some` - --> $DIR/if_then_some_else_none.rs:27:13 + --> $DIR/if_then_some_else_none.rs:26:13 | LL | let _ = if !x { Some(0) } else { None }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _ = if !x { Some(0) } else { None }; = help: consider using `bool::then_some` like: `(!x).then_some(0)` error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:82:13 + --> $DIR/if_then_some_else_none.rs:81:13 | LL | let _ = if foo() { | _____________^ diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs index 331fd29b74e8..f7902e6fd538 100644 --- a/tests/ui/manual_clamp.rs +++ b/tests/ui/manual_clamp.rs @@ -1,4 +1,3 @@ -#![feature(custom_inner_attributes)] #![warn(clippy::manual_clamp)] #![allow( unused, @@ -304,9 +303,8 @@ fn cmp_min_max(input: i32) -> i32 { input * 3 } +#[clippy::msrv = "1.49"] fn msrv_1_49() { - #![clippy::msrv = "1.49"] - let (input, min, max) = (0, -1, 2); let _ = if input < min { min @@ -317,9 +315,8 @@ fn msrv_1_49() { }; } +#[clippy::msrv = "1.50"] fn msrv_1_50() { - #![clippy::msrv = "1.50"] - let (input, min, max) = (0, -1, 2); let _ = if input < min { min diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr index 70abe28091c9..988ad1527e83 100644 --- a/tests/ui/manual_clamp.stderr +++ b/tests/ui/manual_clamp.stderr @@ -1,5 +1,5 @@ error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:77:5 + --> $DIR/manual_clamp.rs:76:5 | LL | / if x9 < min { LL | | x9 = min; @@ -13,7 +13,7 @@ LL | | } = note: `-D clippy::manual-clamp` implied by `-D warnings` error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:92:5 + --> $DIR/manual_clamp.rs:91:5 | LL | / if x11 > max { LL | | x11 = max; @@ -26,7 +26,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:100:5 + --> $DIR/manual_clamp.rs:99:5 | LL | / if min > x12 { LL | | x12 = min; @@ -39,7 +39,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:108:5 + --> $DIR/manual_clamp.rs:107:5 | LL | / if max < x13 { LL | | x13 = max; @@ -52,7 +52,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:162:5 + --> $DIR/manual_clamp.rs:161:5 | LL | / if max < x33 { LL | | x33 = max; @@ -65,7 +65,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:22:14 + --> $DIR/manual_clamp.rs:21:14 | LL | let x0 = if max < input { | ______________^ @@ -80,7 +80,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:30:14 + --> $DIR/manual_clamp.rs:29:14 | LL | let x1 = if input > max { | ______________^ @@ -95,7 +95,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:38:14 + --> $DIR/manual_clamp.rs:37:14 | LL | let x2 = if input < min { | ______________^ @@ -110,7 +110,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:46:14 + --> $DIR/manual_clamp.rs:45:14 | LL | let x3 = if min > input { | ______________^ @@ -125,7 +125,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:54:14 + --> $DIR/manual_clamp.rs:53:14 | LL | let x4 = input.max(min).min(max); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -133,7 +133,7 @@ LL | let x4 = input.max(min).min(max); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:56:14 + --> $DIR/manual_clamp.rs:55:14 | LL | let x5 = input.min(max).max(min); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -141,7 +141,7 @@ LL | let x5 = input.min(max).max(min); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:58:14 + --> $DIR/manual_clamp.rs:57:14 | LL | let x6 = match input { | ______________^ @@ -154,7 +154,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:64:14 + --> $DIR/manual_clamp.rs:63:14 | LL | let x7 = match input { | ______________^ @@ -167,7 +167,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:70:14 + --> $DIR/manual_clamp.rs:69:14 | LL | let x8 = match input { | ______________^ @@ -180,7 +180,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:84:15 + --> $DIR/manual_clamp.rs:83:15 | LL | let x10 = match input { | _______________^ @@ -193,7 +193,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:115:15 + --> $DIR/manual_clamp.rs:114:15 | LL | let x14 = if input > CONST_MAX { | _______________^ @@ -208,7 +208,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:124:19 + --> $DIR/manual_clamp.rs:123:19 | LL | let x15 = if input > max { | ___________________^ @@ -224,7 +224,7 @@ LL | | }; = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:135:19 + --> $DIR/manual_clamp.rs:134:19 | LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -232,7 +232,7 @@ LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:136:19 + --> $DIR/manual_clamp.rs:135:19 | LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -240,7 +240,7 @@ LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:137:19 + --> $DIR/manual_clamp.rs:136:19 | LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -248,7 +248,7 @@ LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:138:19 + --> $DIR/manual_clamp.rs:137:19 | LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -256,7 +256,7 @@ LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:139:19 + --> $DIR/manual_clamp.rs:138:19 | LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -264,7 +264,7 @@ LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:140:19 + --> $DIR/manual_clamp.rs:139:19 | LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -272,7 +272,7 @@ LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:141:19 + --> $DIR/manual_clamp.rs:140:19 | LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -280,7 +280,7 @@ LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:142:19 + --> $DIR/manual_clamp.rs:141:19 | LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -288,7 +288,7 @@ LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:144:19 + --> $DIR/manual_clamp.rs:143:19 | LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -297,7 +297,7 @@ LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:145:19 + --> $DIR/manual_clamp.rs:144:19 | LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -306,7 +306,7 @@ LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:146:19 + --> $DIR/manual_clamp.rs:145:19 | LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -315,7 +315,7 @@ LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:147:19 + --> $DIR/manual_clamp.rs:146:19 | LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -324,7 +324,7 @@ LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:148:19 + --> $DIR/manual_clamp.rs:147:19 | LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -333,7 +333,7 @@ LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:149:19 + --> $DIR/manual_clamp.rs:148:19 | LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -342,7 +342,7 @@ LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:150:19 + --> $DIR/manual_clamp.rs:149:19 | LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -351,7 +351,7 @@ LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:151:19 + --> $DIR/manual_clamp.rs:150:19 | LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -360,7 +360,7 @@ LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:154:5 + --> $DIR/manual_clamp.rs:153:5 | LL | / if x32 < min { LL | | x32 = min; @@ -372,7 +372,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:324:13 + --> $DIR/manual_clamp.rs:321:13 | LL | let _ = if input < min { | _____________^ diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index 765bb785994e..231ba83b1426 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused, dead_code)] #![warn(clippy::manual_is_ascii_check)] @@ -18,28 +17,26 @@ fn main() { assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); } +#[clippy::msrv = "1.23"] fn msrv_1_23() { - #![clippy::msrv = "1.23"] - assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); } +#[clippy::msrv = "1.24"] fn msrv_1_24() { - #![clippy::msrv = "1.24"] - assert!(b'1'.is_ascii_digit()); assert!('X'.is_ascii_uppercase()); assert!('x'.is_ascii_alphabetic()); } +#[clippy::msrv = "1.46"] fn msrv_1_46() { - #![clippy::msrv = "1.46"] const FOO: bool = matches!('x', '0'..='9'); } +#[clippy::msrv = "1.47"] fn msrv_1_47() { - #![clippy::msrv = "1.47"] const FOO: bool = 'x'.is_ascii_digit(); } diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index be1331610412..39ee6151c56f 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused, dead_code)] #![warn(clippy::manual_is_ascii_check)] @@ -18,28 +17,26 @@ fn main() { assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); } +#[clippy::msrv = "1.23"] fn msrv_1_23() { - #![clippy::msrv = "1.23"] - assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); } +#[clippy::msrv = "1.24"] fn msrv_1_24() { - #![clippy::msrv = "1.24"] - assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); } +#[clippy::msrv = "1.46"] fn msrv_1_46() { - #![clippy::msrv = "1.46"] const FOO: bool = matches!('x', '0'..='9'); } +#[clippy::msrv = "1.47"] fn msrv_1_47() { - #![clippy::msrv = "1.47"] const FOO: bool = matches!('x', '0'..='9'); } diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index c0a9d4db1a15..397cbe05c822 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -1,5 +1,5 @@ error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:8:13 + --> $DIR/manual_is_ascii_check.rs:7:13 | LL | assert!(matches!('x', 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_lowercase()` @@ -7,61 +7,61 @@ LL | assert!(matches!('x', 'a'..='z')); = note: `-D clippy::manual-is-ascii-check` implied by `-D warnings` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:9:13 + --> $DIR/manual_is_ascii_check.rs:8:13 | LL | assert!(matches!('X', 'A'..='Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:10:13 + --> $DIR/manual_is_ascii_check.rs:9:13 | LL | assert!(matches!(b'x', b'a'..=b'z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'x'.is_ascii_lowercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:11:13 + --> $DIR/manual_is_ascii_check.rs:10:13 | LL | assert!(matches!(b'X', b'A'..=b'Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:14:13 + --> $DIR/manual_is_ascii_check.rs:13:13 | LL | assert!(matches!(num, '0'..='9')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:15:13 + --> $DIR/manual_is_ascii_check.rs:14:13 | LL | assert!(matches!(b'1', b'0'..=b'9')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:16:13 + --> $DIR/manual_is_ascii_check.rs:15:13 | LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:32:13 + --> $DIR/manual_is_ascii_check.rs:29:13 | LL | assert!(matches!(b'1', b'0'..=b'9')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:33:13 + --> $DIR/manual_is_ascii_check.rs:30:13 | LL | assert!(matches!('X', 'A'..='Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:34:13 + --> $DIR/manual_is_ascii_check.rs:31:13 | LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:44:23 + --> $DIR/manual_is_ascii_check.rs:41:23 | LL | const FOO: bool = matches!('x', '0'..='9'); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed index b942fbfe9305..4cdc0546a744 100644 --- a/tests/ui/manual_rem_euclid.fixed +++ b/tests/ui/manual_rem_euclid.fixed @@ -1,7 +1,6 @@ // run-rustfix // aux-build:macro_rules.rs -#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -55,31 +54,27 @@ pub const fn const_rem_euclid_4(num: i32) -> i32 { num.rem_euclid(4) } +#[clippy::msrv = "1.37"] pub fn msrv_1_37() { - #![clippy::msrv = "1.37"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } +#[clippy::msrv = "1.38"] pub fn msrv_1_38() { - #![clippy::msrv = "1.38"] - let x: i32 = 10; let _: i32 = x.rem_euclid(4); } // For const fns: +#[clippy::msrv = "1.51"] pub const fn msrv_1_51() { - #![clippy::msrv = "1.51"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } +#[clippy::msrv = "1.52"] pub const fn msrv_1_52() { - #![clippy::msrv = "1.52"] - let x: i32 = 10; let _: i32 = x.rem_euclid(4); } diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs index 7462d532169f..58a9e20f38b1 100644 --- a/tests/ui/manual_rem_euclid.rs +++ b/tests/ui/manual_rem_euclid.rs @@ -1,7 +1,6 @@ // run-rustfix // aux-build:macro_rules.rs -#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -55,31 +54,27 @@ pub const fn const_rem_euclid_4(num: i32) -> i32 { ((num % 4) + 4) % 4 } +#[clippy::msrv = "1.37"] pub fn msrv_1_37() { - #![clippy::msrv = "1.37"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } +#[clippy::msrv = "1.38"] pub fn msrv_1_38() { - #![clippy::msrv = "1.38"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } // For const fns: +#[clippy::msrv = "1.51"] pub const fn msrv_1_51() { - #![clippy::msrv = "1.51"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } +#[clippy::msrv = "1.52"] pub const fn msrv_1_52() { - #![clippy::msrv = "1.52"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr index d51bac03b565..e3122a588b64 100644 --- a/tests/ui/manual_rem_euclid.stderr +++ b/tests/ui/manual_rem_euclid.stderr @@ -1,5 +1,5 @@ error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:20:18 + --> $DIR/manual_rem_euclid.rs:19:18 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -7,31 +7,31 @@ LL | let _: i32 = ((value % 4) + 4) % 4; = note: `-D clippy::manual-rem-euclid` implied by `-D warnings` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:21:18 + --> $DIR/manual_rem_euclid.rs:20:18 | LL | let _: i32 = (4 + (value % 4)) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:22:18 + --> $DIR/manual_rem_euclid.rs:21:18 | LL | let _: i32 = (value % 4 + 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:23:18 + --> $DIR/manual_rem_euclid.rs:22:18 | LL | let _: i32 = (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:24:22 + --> $DIR/manual_rem_euclid.rs:23:22 | LL | let _: i32 = 1 + (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:13:22 + --> $DIR/manual_rem_euclid.rs:12:22 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -42,25 +42,25 @@ LL | internal_rem_euclid!(); = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:50:5 + --> $DIR/manual_rem_euclid.rs:49:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:55:5 + --> $DIR/manual_rem_euclid.rs:54:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:69:18 + --> $DIR/manual_rem_euclid.rs:66:18 | LL | let _: i32 = ((x % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:84:18 + --> $DIR/manual_rem_euclid.rs:79:18 | LL | let _: i32 = ((x % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index fba503a20667..e5ae3cf3e503 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -1,5 +1,4 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_retain)] #![allow(unused)] use std::collections::BTreeMap; @@ -216,8 +215,8 @@ fn vec_deque_retain() { bar = foobar.into_iter().filter(|x| x % 2 == 0).collect(); } +#[clippy::msrv = "1.52"] fn _msrv_153() { - #![clippy::msrv = "1.52"] let mut btree_map: BTreeMap = (0..8).map(|x| (x, x * 10)).collect(); btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); @@ -225,14 +224,14 @@ fn _msrv_153() { btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); } +#[clippy::msrv = "1.25"] fn _msrv_126() { - #![clippy::msrv = "1.25"] let mut s = String::from("foobar"); s = s.chars().filter(|&c| c != 'o').to_owned().collect(); } +#[clippy::msrv = "1.17"] fn _msrv_118() { - #![clippy::msrv = "1.17"] let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]); hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); let mut hash_map: HashMap = (0..8).map(|x| (x, x * 10)).collect(); diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index 81a849fe7684..1021f15edd1e 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -1,5 +1,4 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_retain)] #![allow(unused)] use std::collections::BTreeMap; @@ -222,8 +221,8 @@ fn vec_deque_retain() { bar = foobar.into_iter().filter(|x| x % 2 == 0).collect(); } +#[clippy::msrv = "1.52"] fn _msrv_153() { - #![clippy::msrv = "1.52"] let mut btree_map: BTreeMap = (0..8).map(|x| (x, x * 10)).collect(); btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); @@ -231,14 +230,14 @@ fn _msrv_153() { btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); } +#[clippy::msrv = "1.25"] fn _msrv_126() { - #![clippy::msrv = "1.25"] let mut s = String::from("foobar"); s = s.chars().filter(|&c| c != 'o').to_owned().collect(); } +#[clippy::msrv = "1.17"] fn _msrv_118() { - #![clippy::msrv = "1.17"] let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]); hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); let mut hash_map: HashMap = (0..8).map(|x| (x, x * 10)).collect(); diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr index ec635919b48f..89316ce1d998 100644 --- a/tests/ui/manual_retain.stderr +++ b/tests/ui/manual_retain.stderr @@ -1,5 +1,5 @@ error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:52:5 + --> $DIR/manual_retain.rs:51:5 | LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)` @@ -7,13 +7,13 @@ LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect() = note: `-D clippy::manual-retain` implied by `-D warnings` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:53:5 + --> $DIR/manual_retain.rs:52:5 | LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:54:5 + --> $DIR/manual_retain.rs:53:5 | LL | / btree_map = btree_map LL | | .into_iter() @@ -22,37 +22,37 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:76:5 + --> $DIR/manual_retain.rs:75:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:77:5 + --> $DIR/manual_retain.rs:76:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:78:5 + --> $DIR/manual_retain.rs:77:5 | LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:108:5 + --> $DIR/manual_retain.rs:107:5 | LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:109:5 + --> $DIR/manual_retain.rs:108:5 | LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:110:5 + --> $DIR/manual_retain.rs:109:5 | LL | / hash_map = hash_map LL | | .into_iter() @@ -61,61 +61,61 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:131:5 + --> $DIR/manual_retain.rs:130:5 | LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:132:5 + --> $DIR/manual_retain.rs:131:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:133:5 + --> $DIR/manual_retain.rs:132:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:162:5 + --> $DIR/manual_retain.rs:161:5 | LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:174:5 + --> $DIR/manual_retain.rs:173:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:175:5 + --> $DIR/manual_retain.rs:174:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:176:5 + --> $DIR/manual_retain.rs:175:5 | LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:198:5 + --> $DIR/manual_retain.rs:197:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:199:5 + --> $DIR/manual_retain.rs:198:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:200:5 + --> $DIR/manual_retain.rs:199:5 | LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed index c7ca770434a3..50b02019cc27 100644 --- a/tests/ui/manual_split_once.fixed +++ b/tests/ui/manual_split_once.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] @@ -127,8 +126,8 @@ fn indirect() -> Option<()> { None } +#[clippy::msrv = "1.51"] fn _msrv_1_51() { - #![clippy::msrv = "1.51"] // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); @@ -137,8 +136,8 @@ fn _msrv_1_51() { let b = iter.next().unwrap(); } +#[clippy::msrv = "1.52"] fn _msrv_1_52() { - #![clippy::msrv = "1.52"] let _ = "key=value".split_once('=').unwrap().1; let (a, b) = "a.b.c".split_once('.').unwrap(); diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs index ee2848a251ee..e1e8b71a9def 100644 --- a/tests/ui/manual_split_once.rs +++ b/tests/ui/manual_split_once.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] @@ -127,8 +126,8 @@ fn indirect() -> Option<()> { None } +#[clippy::msrv = "1.51"] fn _msrv_1_51() { - #![clippy::msrv = "1.51"] // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); @@ -137,8 +136,8 @@ fn _msrv_1_51() { let b = iter.next().unwrap(); } +#[clippy::msrv = "1.52"] fn _msrv_1_52() { - #![clippy::msrv = "1.52"] let _ = "key=value".splitn(2, '=').nth(1).unwrap(); let mut iter = "a.b.c".splitn(2, '.'); diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index 2696694680ad..78da5a16cc52 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -1,5 +1,5 @@ error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:14:13 + --> $DIR/manual_split_once.rs:13:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` @@ -7,79 +7,79 @@ LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); = note: `-D clippy::manual-split-once` implied by `-D warnings` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:15:13 + --> $DIR/manual_split_once.rs:14:13 | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:16:18 + --> $DIR/manual_split_once.rs:15:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:19:13 + --> $DIR/manual_split_once.rs:18:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:22:13 + --> $DIR/manual_split_once.rs:21:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:25:13 + --> $DIR/manual_split_once.rs:24:13 | LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:28:17 + --> $DIR/manual_split_once.rs:27:17 | LL | let _ = s.splitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:29:17 + --> $DIR/manual_split_once.rs:28:17 | LL | let _ = s.splitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:30:17 + --> $DIR/manual_split_once.rs:29:17 | LL | let _ = s.rsplitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:31:17 + --> $DIR/manual_split_once.rs:30:17 | LL | let _ = s.rsplitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:39:13 + --> $DIR/manual_split_once.rs:38:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:40:18 + --> $DIR/manual_split_once.rs:39:18 | LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:41:13 + --> $DIR/manual_split_once.rs:40:13 | LL | let _ = s.rsplitn(2, '=').nth(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:45:5 + --> $DIR/manual_split_once.rs:44:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +104,7 @@ LL + | error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:49:5 + --> $DIR/manual_split_once.rs:48:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL + | error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:53:5 + --> $DIR/manual_split_once.rs:52:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL + | error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:57:5 + --> $DIR/manual_split_once.rs:56:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -179,13 +179,13 @@ LL + | error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:142:13 + --> $DIR/manual_split_once.rs:141:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:144:5 + --> $DIR/manual_split_once.rs:143:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed index 0704ba2f933e..3d56f2a0dedb 100644 --- a/tests/ui/manual_str_repeat.fixed +++ b/tests/ui/manual_str_repeat.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; @@ -54,13 +53,13 @@ fn main() { let _: String = repeat(x).take(count).collect(); } +#[clippy::msrv = "1.15"] fn _msrv_1_15() { - #![clippy::msrv = "1.15"] // `str::repeat` was stabilized in 1.16. Do not lint this let _: String = std::iter::repeat("test").take(10).collect(); } +#[clippy::msrv = "1.16"] fn _msrv_1_16() { - #![clippy::msrv = "1.16"] let _: String = "test".repeat(10); } diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs index f522be439aa0..e8240a949dbc 100644 --- a/tests/ui/manual_str_repeat.rs +++ b/tests/ui/manual_str_repeat.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; @@ -54,13 +53,13 @@ fn main() { let _: String = repeat(x).take(count).collect(); } +#[clippy::msrv = "1.15"] fn _msrv_1_15() { - #![clippy::msrv = "1.15"] // `str::repeat` was stabilized in 1.16. Do not lint this let _: String = std::iter::repeat("test").take(10).collect(); } +#[clippy::msrv = "1.16"] fn _msrv_1_16() { - #![clippy::msrv = "1.16"] let _: String = std::iter::repeat("test").take(10).collect(); } diff --git a/tests/ui/manual_str_repeat.stderr b/tests/ui/manual_str_repeat.stderr index c65116897164..bdfee7cab261 100644 --- a/tests/ui/manual_str_repeat.stderr +++ b/tests/ui/manual_str_repeat.stderr @@ -1,5 +1,5 @@ error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:10:21 + --> $DIR/manual_str_repeat.rs:9:21 | LL | let _: String = std::iter::repeat("test").take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` @@ -7,55 +7,55 @@ LL | let _: String = std::iter::repeat("test").take(10).collect(); = note: `-D clippy::manual-str-repeat` implied by `-D warnings` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:11:21 + --> $DIR/manual_str_repeat.rs:10:21 | LL | let _: String = std::iter::repeat('x').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:12:21 + --> $DIR/manual_str_repeat.rs:11:21 | LL | let _: String = std::iter::repeat('/'').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:13:21 + --> $DIR/manual_str_repeat.rs:12:21 | LL | let _: String = std::iter::repeat('"').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:17:13 + --> $DIR/manual_str_repeat.rs:16:13 | LL | let _ = repeat(x).take(count + 2).collect::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:26:21 + --> $DIR/manual_str_repeat.rs:25:21 | LL | let _: String = repeat(*x).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:35:21 + --> $DIR/manual_str_repeat.rs:34:21 | LL | let _: String = repeat(x).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:47:21 + --> $DIR/manual_str_repeat.rs:46:21 | LL | let _: String = repeat(Cow::Borrowed("test")).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Cow::Borrowed("test").repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:50:21 + --> $DIR/manual_str_repeat.rs:49:21 | LL | let _: String = repeat(x).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:65:21 + --> $DIR/manual_str_repeat.rs:64:21 | LL | let _: String = std::iter::repeat("test").take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index 85009d78558b..b0b1c262aeed 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -1,4 +1,3 @@ -#![feature(custom_inner_attributes)] #![warn(clippy::manual_strip)] fn main() { @@ -66,18 +65,16 @@ fn main() { } } +#[clippy::msrv = "1.44"] fn msrv_1_44() { - #![clippy::msrv = "1.44"] - let s = "abc"; if s.starts_with('a') { s[1..].to_string(); } } +#[clippy::msrv = "1.45"] fn msrv_1_45() { - #![clippy::msrv = "1.45"] - let s = "abc"; if s.starts_with('a') { s[1..].to_string(); diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index ad2a362f3e76..f592e898fc92 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> $DIR/manual_strip.rs:8:24 + --> $DIR/manual_strip.rs:7:24 | LL | str::to_string(&s["ab".len()..]); | ^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:7:5 + --> $DIR/manual_strip.rs:6:5 | LL | if s.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL ~ .to_string(); | error: stripping a suffix manually - --> $DIR/manual_strip.rs:16:24 + --> $DIR/manual_strip.rs:15:24 | LL | str::to_string(&s[..s.len() - "bc".len()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the suffix was tested here - --> $DIR/manual_strip.rs:15:5 + --> $DIR/manual_strip.rs:14:5 | LL | if s.ends_with("bc") { | ^^^^^^^^^^^^^^^^^^^^^ @@ -42,13 +42,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:25:24 + --> $DIR/manual_strip.rs:24:24 | LL | str::to_string(&s[1..]); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:24:5 + --> $DIR/manual_strip.rs:23:5 | LL | if s.starts_with('a') { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -60,13 +60,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:32:24 + --> $DIR/manual_strip.rs:31:24 | LL | str::to_string(&s[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:31:5 + --> $DIR/manual_strip.rs:30:5 | LL | if s.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,13 +77,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:38:24 + --> $DIR/manual_strip.rs:37:24 | LL | str::to_string(&s[PREFIX.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:37:5 + --> $DIR/manual_strip.rs:36:5 | LL | if s.starts_with(PREFIX) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,13 +95,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:45:24 + --> $DIR/manual_strip.rs:44:24 | LL | str::to_string(&TARGET[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:44:5 + --> $DIR/manual_strip.rs:43:5 | LL | if TARGET.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,13 +112,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:51:9 + --> $DIR/manual_strip.rs:50:9 | LL | s1[2..].to_uppercase(); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:50:5 + --> $DIR/manual_strip.rs:49:5 | LL | if s1.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,13 +129,13 @@ LL ~ .to_uppercase(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:83:9 + --> $DIR/manual_strip.rs:80:9 | LL | s[1..].to_string(); | ^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:82:5 + --> $DIR/manual_strip.rs:79:5 | LL | if s.starts_with('a') { | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 396b22a9abb3..32631024ca5d 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,5 @@ // aux-build:option_helpers.rs -#![feature(custom_inner_attributes)] #![warn(clippy::map_unwrap_or)] #![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)] @@ -82,17 +81,15 @@ fn main() { result_methods(); } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - let res: Result = Ok(1); let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); } +#[clippy::msrv = "1.41"] fn msrv_1_41() { - #![clippy::msrv = "1.41"] - let res: Result = Ok(1); let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index d17d24a403ea..41781b050fa2 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,5 +1,5 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:18:13 + --> $DIR/map_unwrap_or.rs:17:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -15,7 +15,7 @@ LL + let _ = opt.map_or(0, |x| x + 1); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:22:13 + --> $DIR/map_unwrap_or.rs:21:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -33,7 +33,7 @@ LL ~ ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:26:13 + --> $DIR/map_unwrap_or.rs:25:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -50,7 +50,7 @@ LL ~ }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:31:13 + --> $DIR/map_unwrap_or.rs:30:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + let _ = opt.and_then(|x| Some(x + 1)); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:33:13 + --> $DIR/map_unwrap_or.rs:32:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -80,7 +80,7 @@ LL ~ ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:37:13 + --> $DIR/map_unwrap_or.rs:36:13 | LL | let _ = opt | _____________^ @@ -95,7 +95,7 @@ LL + .and_then(|x| Some(x + 1)); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:48:13 + --> $DIR/map_unwrap_or.rs:47:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:52:13 + --> $DIR/map_unwrap_or.rs:51:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -117,7 +117,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:56:13 + --> $DIR/map_unwrap_or.rs:55:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -127,7 +127,7 @@ LL | | ); | |_________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:68:13 + --> $DIR/map_unwrap_or.rs:67:13 | LL | let _ = res.map(|x| { | _____________^ @@ -137,7 +137,7 @@ LL | | ).unwrap_or_else(|_e| 0); | |____________________________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:72:13 + --> $DIR/map_unwrap_or.rs:71:13 | LL | let _ = res.map(|x| x + 1) | _____________^ @@ -147,7 +147,7 @@ LL | | }); | |__________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:98:13 + --> $DIR/map_unwrap_or.rs:95:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 968f462f8a02..55cd15bd5c38 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, @@ -200,17 +199,15 @@ fn main() { }; } +#[clippy::msrv = "1.41"] fn msrv_1_41() { - #![clippy::msrv = "1.41"] - let _y = match Some(5) { Some(0) => true, _ => false, }; } +#[clippy::msrv = "1.42"] fn msrv_1_42() { - #![clippy::msrv = "1.42"] - let _y = matches!(Some(5), Some(0)); } diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index c6b479e27c5a..5d645e108e51 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, @@ -241,18 +240,16 @@ fn main() { }; } +#[clippy::msrv = "1.41"] fn msrv_1_41() { - #![clippy::msrv = "1.41"] - let _y = match Some(5) { Some(0) => true, _ => false, }; } +#[clippy::msrv = "1.42"] fn msrv_1_42() { - #![clippy::msrv = "1.42"] - let _y = match Some(5) { Some(0) => true, _ => false, diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index a4df8008ac23..46f67ef4900f 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:16:14 + --> $DIR/match_expr_like_matches_macro.rs:15:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:22:14 + --> $DIR/match_expr_like_matches_macro.rs:21:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:28:14 + --> $DIR/match_expr_like_matches_macro.rs:27:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:34:15 + --> $DIR/match_expr_like_matches_macro.rs:33:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:40:16 + --> $DIR/match_expr_like_matches_macro.rs:39:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:64:20 + --> $DIR/match_expr_like_matches_macro.rs:63:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:74:20 + --> $DIR/match_expr_like_matches_macro.rs:73:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:84:20 + --> $DIR/match_expr_like_matches_macro.rs:83:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:144:18 + --> $DIR/match_expr_like_matches_macro.rs:143:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:153:18 + --> $DIR/match_expr_like_matches_macro.rs:152:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:170:21 + --> $DIR/match_expr_like_matches_macro.rs:169:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:184:20 + --> $DIR/match_expr_like_matches_macro.rs:183:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:196:20 + --> $DIR/match_expr_like_matches_macro.rs:195:20 | LL | let _res = match &val { | ____________________^ @@ -134,7 +134,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:256:14 + --> $DIR/match_expr_like_matches_macro.rs:253:14 | LL | let _y = match Some(5) { | ______________^ diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index ae237395b95f..874d55843303 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] #![warn( clippy::all, @@ -80,16 +79,14 @@ fn main() { dont_lint_primitive(); } +#[clippy::msrv = "1.39"] fn msrv_1_39() { - #![clippy::msrv = "1.39"] - let mut s = String::from("foo"); let _ = std::mem::replace(&mut s, String::default()); } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - let mut s = String::from("foo"); let _ = std::mem::take(&mut s); } diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 3202e99e0be9..f4f3bff51446 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] #![warn( clippy::all, @@ -80,16 +79,14 @@ fn main() { dont_lint_primitive(); } +#[clippy::msrv = "1.39"] fn msrv_1_39() { - #![clippy::msrv = "1.39"] - let mut s = String::from("foo"); let _ = std::mem::replace(&mut s, String::default()); } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - let mut s = String::from("foo"); let _ = std::mem::replace(&mut s, String::default()); } diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index dd8a50dab900..caa127f76eef 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:17:13 + --> $DIR/mem_replace.rs:16:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:19:13 + --> $DIR/mem_replace.rs:18:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:24:13 + --> $DIR/mem_replace.rs:23:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,103 +21,103 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:27:13 + --> $DIR/mem_replace.rs:26:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:28:13 + --> $DIR/mem_replace.rs:27:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:31:13 + --> $DIR/mem_replace.rs:30:13 | LL | let _ = std::mem::replace(&mut v, Vec::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:32:13 + --> $DIR/mem_replace.rs:31:13 | LL | let _ = std::mem::replace(&mut v, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:33:13 + --> $DIR/mem_replace.rs:32:13 | LL | let _ = std::mem::replace(&mut v, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:34:13 + --> $DIR/mem_replace.rs:33:13 | LL | let _ = std::mem::replace(&mut v, vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:37:13 + --> $DIR/mem_replace.rs:36:13 | LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:40:13 + --> $DIR/mem_replace.rs:39:13 | LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:43:13 + --> $DIR/mem_replace.rs:42:13 | LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:46:13 + --> $DIR/mem_replace.rs:45:13 | LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:49:13 + --> $DIR/mem_replace.rs:48:13 | LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:52:13 + --> $DIR/mem_replace.rs:51:13 | LL | let _ = std::mem::replace(&mut list, LinkedList::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:55:13 + --> $DIR/mem_replace.rs:54:13 | LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:58:13 + --> $DIR/mem_replace.rs:57:13 | LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:61:13 + --> $DIR/mem_replace.rs:60:13 | LL | let _ = std::mem::replace(&mut refstr, ""); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:64:13 + --> $DIR/mem_replace.rs:63:13 | LL | let _ = std::mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:94:13 + --> $DIR/mem_replace.rs:91:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 28ab132394b8..955e7eb72763 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -3,27 +3,37 @@ fn main() {} +#[clippy::msrv = "1.42.0"] fn just_under_msrv() { - #![clippy::msrv = "1.42.0"] let log2_10 = 3.321928094887362; } +#[clippy::msrv = "1.43.0"] fn meets_msrv() { - #![clippy::msrv = "1.43.0"] let log2_10 = 3.321928094887362; } +#[clippy::msrv = "1.44.0"] fn just_above_msrv() { - #![clippy::msrv = "1.44.0"] let log2_10 = 3.321928094887362; } +#[clippy::msrv = "1.42"] fn no_patch_under() { - #![clippy::msrv = "1.42"] let log2_10 = 3.321928094887362; } +#[clippy::msrv = "1.43"] fn no_patch_meets() { + let log2_10 = 3.321928094887362; +} + +fn inner_attr_under() { + #![clippy::msrv = "1.42"] + let log2_10 = 3.321928094887362; +} + +fn inner_attr_meets() { #![clippy::msrv = "1.43"] let log2_10 = 3.321928094887362; } diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 6174443372f8..7e2135584efd 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -32,12 +32,20 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> $DIR/min_rust_version_attr.rs:45:27 + --> $DIR/min_rust_version_attr.rs:48:19 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:55:27 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ | = help: consider using the constant directly -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr index 93370a0fa9c9..675b78031525 100644 --- a/tests/ui/min_rust_version_invalid_attr.stderr +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -4,7 +4,7 @@ error: `invalid.version` is not a valid Rust version LL | #![clippy::msrv = "invalid.version"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `msrv` cannot be an outer attribute +error: `invalid.version` is not a valid Rust version --> $DIR/min_rust_version_invalid_attr.rs:6:1 | LL | #[clippy::msrv = "invalid.version"] diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index b950248ef942..75cace181675 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -7,7 +7,6 @@ #![warn(clippy::missing_const_for_fn)] #![feature(start)] -#![feature(custom_inner_attributes)] extern crate helper; extern crate proc_macro_with_span; @@ -115,9 +114,8 @@ fn unstably_const_fn() { helper::unstably_const_fn() } +#[clippy::msrv = "1.46.0"] mod const_fn_stabilized_after_msrv { - #![clippy::msrv = "1.46.0"] - // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0. fn const_fn_stabilized_after_msrv(byte: u8) { byte.is_ascii_digit(); diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index b85e88784918..0246c8622ed3 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,6 +1,5 @@ #![warn(clippy::missing_const_for_fn)] #![allow(incomplete_features, clippy::let_and_return)] -#![feature(custom_inner_attributes)] use std::mem::transmute; @@ -68,24 +67,21 @@ mod with_drop { } } +#[clippy::msrv = "1.47.0"] mod const_fn_stabilized_before_msrv { - #![clippy::msrv = "1.47.0"] - // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47. fn const_fn_stabilized_before_msrv(byte: u8) { byte.is_ascii_digit(); } } +#[clippy::msrv = "1.45"] fn msrv_1_45() -> i32 { - #![clippy::msrv = "1.45"] - 45 } +#[clippy::msrv = "1.46"] fn msrv_1_46() -> i32 { - #![clippy::msrv = "1.46"] - 46 } diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index f8e221c82f1a..955e1ed26340 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -1,5 +1,5 @@ error: this could be a `const fn` - --> $DIR/could_be_const.rs:13:5 + --> $DIR/could_be_const.rs:12:5 | LL | / pub fn new() -> Self { LL | | Self { guess: 42 } @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` error: this could be a `const fn` - --> $DIR/could_be_const.rs:17:5 + --> $DIR/could_be_const.rs:16:5 | LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { LL | | b @@ -17,7 +17,7 @@ LL | | } | |_____^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:23:1 + --> $DIR/could_be_const.rs:22:1 | LL | / fn one() -> i32 { LL | | 1 @@ -25,7 +25,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:28:1 + --> $DIR/could_be_const.rs:27:1 | LL | / fn two() -> i32 { LL | | let abc = 2; @@ -34,7 +34,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:34:1 + --> $DIR/could_be_const.rs:33:1 | LL | / fn string() -> String { LL | | String::new() @@ -42,7 +42,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:39:1 + --> $DIR/could_be_const.rs:38:1 | LL | / unsafe fn four() -> i32 { LL | | 4 @@ -50,7 +50,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:44:1 + --> $DIR/could_be_const.rs:43:1 | LL | / fn generic(t: T) -> T { LL | | t @@ -58,7 +58,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:52:1 + --> $DIR/could_be_const.rs:51:1 | LL | / fn generic_arr(t: [T; 1]) -> T { LL | | t[0] @@ -66,7 +66,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:65:9 + --> $DIR/could_be_const.rs:64:9 | LL | / pub fn b(self, a: &A) -> B { LL | | B @@ -74,7 +74,7 @@ LL | | } | |_________^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:75:5 + --> $DIR/could_be_const.rs:73:5 | LL | / fn const_fn_stabilized_before_msrv(byte: u8) { LL | | byte.is_ascii_digit(); @@ -82,11 +82,9 @@ LL | | } | |_____^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:86:1 + --> $DIR/could_be_const.rs:84:1 | LL | / fn msrv_1_46() -> i32 { -LL | | #![clippy::msrv = "1.46"] -LL | | LL | | 46 LL | | } | |_^ diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 85b6b639d554..4cb7f6b687f1 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,13 +1,13 @@ // run-rustfix -#![feature(custom_inner_attributes, lint_reasons)] - -#[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables)] -#[allow( +#![feature(lint_reasons)] +#![allow( + unused, clippy::uninlined_format_args, clippy::unnecessary_mut_passed, clippy::unnecessary_to_owned )] +#![warn(clippy::needless_borrow)] + fn main() { let a = 5; let ref_a = &a; @@ -171,14 +171,12 @@ impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} -#[allow(dead_code)] fn check_expect_suppression() { let a = 5; #[expect(clippy::needless_borrow)] let _ = x(&&a); } -#[allow(dead_code)] mod issue9160 { pub struct S { f: F, @@ -267,7 +265,6 @@ where } // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -#[allow(dead_code)] mod copyable_iterator { #[derive(Clone, Copy)] struct Iter; @@ -287,25 +284,20 @@ mod copyable_iterator { } } +#[clippy::msrv = "1.52.0"] mod under_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.52.0"] - fn foo() { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } +#[clippy::msrv = "1.53.0"] mod meets_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.53.0"] - fn foo() { let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); } } -#[allow(unused)] fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -334,7 +326,6 @@ fn issue9383() { } } -#[allow(dead_code)] fn closure_test() { let env = "env".to_owned(); let arg = "arg".to_owned(); @@ -348,7 +339,6 @@ fn closure_test() { f(arg); } -#[allow(dead_code)] mod significant_drop { #[derive(Debug)] struct X; @@ -368,7 +358,6 @@ mod significant_drop { fn debug(_: impl std::fmt::Debug) {} } -#[allow(dead_code)] mod used_exactly_once { fn foo(x: String) { use_x(x); @@ -376,7 +365,6 @@ mod used_exactly_once { fn use_x(_: impl AsRef) {} } -#[allow(dead_code)] mod used_more_than_once { fn foo(x: String) { use_x(&x); @@ -387,7 +375,6 @@ mod used_more_than_once { } // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 -#[allow(dead_code)] mod issue_9111 { struct A; @@ -409,7 +396,6 @@ mod issue_9111 { } } -#[allow(dead_code)] mod issue_9710 { fn main() { let string = String::new(); @@ -421,7 +407,6 @@ mod issue_9710 { fn f>(_: T) {} } -#[allow(dead_code)] mod issue_9739 { fn foo(_it: impl IntoIterator) {} @@ -434,7 +419,6 @@ mod issue_9739 { } } -#[allow(dead_code)] mod issue_9739_method_variant { struct S; @@ -451,7 +435,6 @@ mod issue_9739_method_variant { } } -#[allow(dead_code)] mod issue_9782 { fn foo>(t: T) { println!("{}", std::mem::size_of::()); @@ -475,7 +458,6 @@ mod issue_9782 { } } -#[allow(dead_code)] mod issue_9782_type_relative_variant { struct S; @@ -493,7 +475,6 @@ mod issue_9782_type_relative_variant { } } -#[allow(dead_code)] mod issue_9782_method_variant { struct S; diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 7b97bcf3817e..9a01190ed8db 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,13 +1,13 @@ // run-rustfix -#![feature(custom_inner_attributes, lint_reasons)] - -#[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables)] -#[allow( +#![feature(lint_reasons)] +#![allow( + unused, clippy::uninlined_format_args, clippy::unnecessary_mut_passed, clippy::unnecessary_to_owned )] +#![warn(clippy::needless_borrow)] + fn main() { let a = 5; let ref_a = &a; @@ -171,14 +171,12 @@ impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} -#[allow(dead_code)] fn check_expect_suppression() { let a = 5; #[expect(clippy::needless_borrow)] let _ = x(&&a); } -#[allow(dead_code)] mod issue9160 { pub struct S { f: F, @@ -267,7 +265,6 @@ where } // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -#[allow(dead_code)] mod copyable_iterator { #[derive(Clone, Copy)] struct Iter; @@ -287,25 +284,20 @@ mod copyable_iterator { } } +#[clippy::msrv = "1.52.0"] mod under_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.52.0"] - fn foo() { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } +#[clippy::msrv = "1.53.0"] mod meets_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.53.0"] - fn foo() { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } -#[allow(unused)] fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -334,7 +326,6 @@ fn issue9383() { } } -#[allow(dead_code)] fn closure_test() { let env = "env".to_owned(); let arg = "arg".to_owned(); @@ -348,7 +339,6 @@ fn closure_test() { f(arg); } -#[allow(dead_code)] mod significant_drop { #[derive(Debug)] struct X; @@ -368,7 +358,6 @@ mod significant_drop { fn debug(_: impl std::fmt::Debug) {} } -#[allow(dead_code)] mod used_exactly_once { fn foo(x: String) { use_x(&x); @@ -376,7 +365,6 @@ mod used_exactly_once { fn use_x(_: impl AsRef) {} } -#[allow(dead_code)] mod used_more_than_once { fn foo(x: String) { use_x(&x); @@ -387,7 +375,6 @@ mod used_more_than_once { } // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 -#[allow(dead_code)] mod issue_9111 { struct A; @@ -409,7 +396,6 @@ mod issue_9111 { } } -#[allow(dead_code)] mod issue_9710 { fn main() { let string = String::new(); @@ -421,7 +407,6 @@ mod issue_9710 { fn f>(_: T) {} } -#[allow(dead_code)] mod issue_9739 { fn foo(_it: impl IntoIterator) {} @@ -434,7 +419,6 @@ mod issue_9739 { } } -#[allow(dead_code)] mod issue_9739_method_variant { struct S; @@ -451,7 +435,6 @@ mod issue_9739_method_variant { } } -#[allow(dead_code)] mod issue_9782 { fn foo>(t: T) { println!("{}", std::mem::size_of::()); @@ -475,7 +458,6 @@ mod issue_9782 { } } -#[allow(dead_code)] mod issue_9782_type_relative_variant { struct S; @@ -493,7 +475,6 @@ mod issue_9782_type_relative_variant { } } -#[allow(dead_code)] mod issue_9782_method_variant { struct S; diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 485e6b84c868..d26c317124b8 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -163,55 +163,55 @@ LL | let _ = std::fs::write("x", &"".to_string()); | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:192:13 + --> $DIR/needless_borrow.rs:190:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:201:13 + --> $DIR/needless_borrow.rs:199:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:286:20 + --> $DIR/needless_borrow.rs:283:20 | LL | takes_iter(&mut x) | ^^^^^^ help: change this to: `x` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:304:55 + --> $DIR/needless_borrow.rs:297:55 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:344:37 + --> $DIR/needless_borrow.rs:335:37 | LL | let _ = std::fs::write("x", &arg); | ^^^^ help: change this to: `arg` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:345:37 + --> $DIR/needless_borrow.rs:336:37 | LL | let _ = std::fs::write("x", &loc); | ^^^^ help: change this to: `loc` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:364:15 + --> $DIR/needless_borrow.rs:354:15 | LL | debug(&x); | ^^ help: change this to: `x` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:374:15 + --> $DIR/needless_borrow.rs:363:15 | LL | use_x(&x); | ^^ help: change this to: `x` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:474:13 + --> $DIR/needless_borrow.rs:457:13 | LL | foo(&a); | ^^ help: change this to: `a` diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index ba9d15e59d0e..7eaca571992f 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -8,7 +8,6 @@ dead_code, unused_must_use )] -#![feature(custom_inner_attributes)] struct TO { magic: Option, diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 3a6523e8fe87..960bc7b78983 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -8,7 +8,6 @@ dead_code, unused_must_use )] -#![feature(custom_inner_attributes)] struct TO { magic: Option, diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index f8308e24e771..d1f89e326c67 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -1,5 +1,5 @@ error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:23:12 + --> $DIR/needless_question_mark.rs:22:12 | LL | return Some(to.magic?); | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` @@ -7,67 +7,67 @@ LL | return Some(to.magic?); = note: `-D clippy::needless-question-mark` implied by `-D warnings` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:31:12 + --> $DIR/needless_question_mark.rs:30:12 | LL | return Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:36:5 + --> $DIR/needless_question_mark.rs:35:5 | LL | Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:41:21 + --> $DIR/needless_question_mark.rs:40:21 | LL | to.and_then(|t| Some(t.magic?)) | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:50:9 + --> $DIR/needless_question_mark.rs:49:9 | LL | Some(t.magic?) | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:55:12 + --> $DIR/needless_question_mark.rs:54:12 | LL | return Ok(tr.magic?); | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:62:12 + --> $DIR/needless_question_mark.rs:61:12 | LL | return Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:66:5 + --> $DIR/needless_question_mark.rs:65:5 | LL | Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:70:21 + --> $DIR/needless_question_mark.rs:69:21 | LL | tr.and_then(|t| Ok(t.magic?)) | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:78:9 + --> $DIR/needless_question_mark.rs:77:9 | LL | Ok(t.magic?) | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:85:16 + --> $DIR/needless_question_mark.rs:84:16 | LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:120:27 + --> $DIR/needless_question_mark.rs:119:27 | LL | || -> Option<_> { Some(Some($expr)?) }() | ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)` @@ -78,13 +78,13 @@ LL | let _x = some_and_qmark_in_macro!(x?); = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:131:5 + --> $DIR/needless_question_mark.rs:130:5 | LL | Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:139:5 + --> $DIR/needless_question_mark.rs:138:5 | LL | Ok(s.magic?) | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic` diff --git a/tests/ui/needless_splitn.fixed b/tests/ui/needless_splitn.fixed index 61f5fc4e679e..5496031fefab 100644 --- a/tests/ui/needless_splitn.fixed +++ b/tests/ui/needless_splitn.fixed @@ -1,7 +1,6 @@ // run-rustfix // edition:2018 -#![feature(custom_inner_attributes)] #![warn(clippy::needless_splitn)] #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)] @@ -40,8 +39,8 @@ fn _question_mark(s: &str) -> Option<()> { Some(()) } +#[clippy::msrv = "1.51"] fn _test_msrv() { - #![clippy::msrv = "1.51"] // `manual_split_once` MSRV shouldn't apply to `needless_splitn` let _ = "key=value".split('=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.rs b/tests/ui/needless_splitn.rs index 71d9a7077faa..35c2465bae13 100644 --- a/tests/ui/needless_splitn.rs +++ b/tests/ui/needless_splitn.rs @@ -1,7 +1,6 @@ // run-rustfix // edition:2018 -#![feature(custom_inner_attributes)] #![warn(clippy::needless_splitn)] #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)] @@ -40,8 +39,8 @@ fn _question_mark(s: &str) -> Option<()> { Some(()) } +#[clippy::msrv = "1.51"] fn _test_msrv() { - #![clippy::msrv = "1.51"] // `manual_split_once` MSRV shouldn't apply to `needless_splitn` let _ = "key=value".splitn(2, '=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.stderr b/tests/ui/needless_splitn.stderr index f112b29e7f20..f607d8e1ab5f 100644 --- a/tests/ui/needless_splitn.stderr +++ b/tests/ui/needless_splitn.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:15:13 + --> $DIR/needless_splitn.rs:14:13 | LL | let _ = str.splitn(2, '=').next(); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` @@ -7,73 +7,73 @@ LL | let _ = str.splitn(2, '=').next(); = note: `-D clippy::needless-splitn` implied by `-D warnings` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:16:13 + --> $DIR/needless_splitn.rs:15:13 | LL | let _ = str.splitn(2, '=').nth(0); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:19:18 + --> $DIR/needless_splitn.rs:18:18 | LL | let (_, _) = str.splitn(3, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:22:13 + --> $DIR/needless_splitn.rs:21:13 | LL | let _ = str.rsplitn(2, '=').next(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:23:13 + --> $DIR/needless_splitn.rs:22:13 | LL | let _ = str.rsplitn(2, '=').nth(0); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:26:18 + --> $DIR/needless_splitn.rs:25:18 | LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:28:13 + --> $DIR/needless_splitn.rs:27:13 | LL | let _ = str.splitn(5, '=').next(); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:29:13 + --> $DIR/needless_splitn.rs:28:13 | LL | let _ = str.splitn(5, '=').nth(3); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:35:13 + --> $DIR/needless_splitn.rs:34:13 | LL | let _ = s.splitn(2, '=').next()?; | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:36:13 + --> $DIR/needless_splitn.rs:35:13 | LL | let _ = s.splitn(2, '=').nth(0)?; | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:37:13 + --> $DIR/needless_splitn.rs:36:13 | LL | let _ = s.rsplitn(2, '=').next()?; | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:38:13 + --> $DIR/needless_splitn.rs:37:13 | LL | let _ = s.rsplitn(2, '=').nth(0)?; | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:46:13 + --> $DIR/needless_splitn.rs:45:13 | LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')` diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index bc376d0d7fb3..d124d133faa2 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] @@ -44,16 +43,14 @@ fn main() { let _ = opt.as_deref(); } +#[clippy::msrv = "1.39"] fn msrv_1_39() { - #![clippy::msrv = "1.39"] - let opt = Some(String::from("123")); let _ = opt.as_ref().map(String::as_str); } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - let opt = Some(String::from("123")); let _ = opt.as_deref(); } diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index ba3a2eedc225..86e354c6716b 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] @@ -47,16 +46,14 @@ fn main() { let _ = opt.as_ref().map(std::ops::Deref::deref); } +#[clippy::msrv = "1.39"] fn msrv_1_39() { - #![clippy::msrv = "1.39"] - let opt = Some(String::from("123")); let _ = opt.as_ref().map(String::as_str); } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - let opt = Some(String::from("123")); let _ = opt.as_ref().map(String::as_str); } diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index 7de8b3b6ba43..e471b56eea81 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -1,5 +1,5 @@ error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:14:13 + --> $DIR/option_as_ref_deref.rs:13:13 | LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` @@ -7,7 +7,7 @@ LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:17:13 + --> $DIR/option_as_ref_deref.rs:16:13 | LL | let _ = opt.clone() | _____________^ @@ -17,97 +17,97 @@ LL | | ) | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:23:13 + --> $DIR/option_as_ref_deref.rs:22:13 | LL | let _ = opt.as_mut().map(DerefMut::deref_mut); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:25:13 + --> $DIR/option_as_ref_deref.rs:24:13 | LL | let _ = opt.as_ref().map(String::as_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:26:13 + --> $DIR/option_as_ref_deref.rs:25:13 | LL | let _ = opt.as_ref().map(|x| x.as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:27:13 + --> $DIR/option_as_ref_deref.rs:26:13 | LL | let _ = opt.as_mut().map(String::as_mut_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:28:13 + --> $DIR/option_as_ref_deref.rs:27:13 | LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:29:13 + --> $DIR/option_as_ref_deref.rs:28:13 | LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:30:13 + --> $DIR/option_as_ref_deref.rs:29:13 | LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:31:13 + --> $DIR/option_as_ref_deref.rs:30:13 | LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:32:13 + --> $DIR/option_as_ref_deref.rs:31:13 | LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:33:13 + --> $DIR/option_as_ref_deref.rs:32:13 | LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:35:13 + --> $DIR/option_as_ref_deref.rs:34:13 | LL | let _ = opt.as_ref().map(|x| x.deref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:36:13 + --> $DIR/option_as_ref_deref.rs:35:13 | LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:43:13 + --> $DIR/option_as_ref_deref.rs:42:13 | LL | let _ = opt.as_ref().map(|x| &**x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:44:13 + --> $DIR/option_as_ref_deref.rs:43:13 | LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:47:13 + --> $DIR/option_as_ref_deref.rs:46:13 | LL | let _ = opt.as_ref().map(std::ops::Deref::deref); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:61:13 + --> $DIR/option_as_ref_deref.rs:58:13 | LL | let _ = opt.as_ref().map(String::as_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed index bea6be66a8e0..df36a9b842bf 100644 --- a/tests/ui/ptr_as_ptr.fixed +++ b/tests/ui/ptr_as_ptr.fixed @@ -2,7 +2,6 @@ // aux-build:macro_rules.rs #![warn(clippy::ptr_as_ptr)] -#![feature(custom_inner_attributes)] extern crate macro_rules; @@ -45,8 +44,8 @@ fn main() { let _ = macro_rules::ptr_as_ptr_cast!(ptr); } +#[clippy::msrv = "1.37"] fn _msrv_1_37() { - #![clippy::msrv = "1.37"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; @@ -55,8 +54,8 @@ fn _msrv_1_37() { let _ = mut_ptr as *mut i32; } +#[clippy::msrv = "1.38"] fn _msrv_1_38() { - #![clippy::msrv = "1.38"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs index ca2616b0069a..302c66462d9b 100644 --- a/tests/ui/ptr_as_ptr.rs +++ b/tests/ui/ptr_as_ptr.rs @@ -2,7 +2,6 @@ // aux-build:macro_rules.rs #![warn(clippy::ptr_as_ptr)] -#![feature(custom_inner_attributes)] extern crate macro_rules; @@ -45,8 +44,8 @@ fn main() { let _ = macro_rules::ptr_as_ptr_cast!(ptr); } +#[clippy::msrv = "1.37"] fn _msrv_1_37() { - #![clippy::msrv = "1.37"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; @@ -55,8 +54,8 @@ fn _msrv_1_37() { let _ = mut_ptr as *mut i32; } +#[clippy::msrv = "1.38"] fn _msrv_1_38() { - #![clippy::msrv = "1.38"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr index c58c55cfd83a..a68e1cab6d35 100644 --- a/tests/ui/ptr_as_ptr.stderr +++ b/tests/ui/ptr_as_ptr.stderr @@ -1,5 +1,5 @@ error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:19:13 + --> $DIR/ptr_as_ptr.rs:18:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` @@ -7,31 +7,31 @@ LL | let _ = ptr as *const i32; = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:20:13 + --> $DIR/ptr_as_ptr.rs:19:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:25:17 + --> $DIR/ptr_as_ptr.rs:24:17 | LL | let _ = *ptr_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:38:25 + --> $DIR/ptr_as_ptr.rs:37:25 | LL | let _: *const i32 = ptr as *const _; | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:39:23 + --> $DIR/ptr_as_ptr.rs:38:23 | LL | let _: *mut i32 = mut_ptr as _; | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:11:9 + --> $DIR/ptr_as_ptr.rs:10:9 | LL | $ptr as *const i32 | ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::()` @@ -42,13 +42,13 @@ LL | let _ = cast_it!(ptr); = note: this error originates in the macro `cast_it` (in Nightly builds, run with -Z macro-backtrace for more info) error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:63:13 + --> $DIR/ptr_as_ptr.rs:62:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:64:13 + --> $DIR/ptr_as_ptr.rs:63:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 824f00cb99e8..4923731fe45e 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_range_contains)] #![allow(unused)] #![allow(clippy::no_effect)] @@ -65,16 +64,14 @@ pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let x = 5; x >= 8 && x < 34; } +#[clippy::msrv = "1.35"] fn msrv_1_35() { - #![clippy::msrv = "1.35"] - let x = 5; (8..35).contains(&x); } diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index df925eeadfe5..d623ccb5da63 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_range_contains)] #![allow(unused)] #![allow(clippy::no_effect)] @@ -65,16 +64,14 @@ pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let x = 5; x >= 8 && x < 34; } +#[clippy::msrv = "1.35"] fn msrv_1_35() { - #![clippy::msrv = "1.35"] - let x = 5; x >= 8 && x < 35; } diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 9689e665b05c..ea34023a4664 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -1,5 +1,5 @@ error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:14:5 + --> $DIR/range_contains.rs:13:5 | LL | x >= 8 && x < 12; | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` @@ -7,121 +7,121 @@ LL | x >= 8 && x < 12; = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:15:5 + --> $DIR/range_contains.rs:14:5 | LL | x < 42 && x >= 21; | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:16:5 + --> $DIR/range_contains.rs:15:5 | LL | 100 > x && 1 <= x; | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:19:5 + --> $DIR/range_contains.rs:18:5 | LL | x >= 9 && x <= 99; | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:20:5 + --> $DIR/range_contains.rs:19:5 | LL | x <= 33 && x >= 1; | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:21:5 + --> $DIR/range_contains.rs:20:5 | LL | 999 >= x && 1 <= x; | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:24:5 + --> $DIR/range_contains.rs:23:5 | LL | x < 8 || x >= 12; | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:25:5 + --> $DIR/range_contains.rs:24:5 | LL | x >= 42 || x < 21; | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:26:5 + --> $DIR/range_contains.rs:25:5 | LL | 100 <= x || 1 > x; | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:29:5 + --> $DIR/range_contains.rs:28:5 | LL | x < 9 || x > 99; | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:30:5 + --> $DIR/range_contains.rs:29:5 | LL | x > 33 || x < 1; | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:31:5 + --> $DIR/range_contains.rs:30:5 | LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:46:5 + --> $DIR/range_contains.rs:45:5 | LL | y >= 0. && y < 1.; | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:47:5 + --> $DIR/range_contains.rs:46:5 | LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:50:5 + --> $DIR/range_contains.rs:49:5 | LL | x >= -10 && x <= 10; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:52:5 + --> $DIR/range_contains.rs:51:5 | LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:57:30 + --> $DIR/range_contains.rs:56:30 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:57:5 + --> $DIR/range_contains.rs:56:5 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:58:29 + --> $DIR/range_contains.rs:57:29 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:58:5 + --> $DIR/range_contains.rs:57:5 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:79:5 + --> $DIR/range_contains.rs:76:5 | LL | x >= 8 && x < 35; | ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)` diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index 34ab552cb1d8..ec7f8ae923a7 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -72,16 +71,14 @@ fn issue_3476() { S { foo: foo:: }; } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - let start = 0; let _ = RangeFrom { start: start }; } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - let start = 0; let _ = RangeFrom { start }; } diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index a051b1f96f0f..73122016cf69 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -72,16 +71,14 @@ fn issue_3476() { S { foo: foo:: }; } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - let start = 0; let _ = RangeFrom { start: start }; } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - let start = 0; let _ = RangeFrom { start: start }; } diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 8b82e062b93a..00a72c50cf7d 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -1,5 +1,5 @@ error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:36:9 + --> $DIR/redundant_field_names.rs:35:9 | LL | gender: gender, | ^^^^^^^^^^^^^^ help: replace it with: `gender` @@ -7,43 +7,43 @@ LL | gender: gender, = note: `-D clippy::redundant-field-names` implied by `-D warnings` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:37:9 + --> $DIR/redundant_field_names.rs:36:9 | LL | age: age, | ^^^^^^^^ help: replace it with: `age` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:25 + --> $DIR/redundant_field_names.rs:57:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:59:23 + --> $DIR/redundant_field_names.rs:58:23 | LL | let _ = RangeTo { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:60:21 + --> $DIR/redundant_field_names.rs:59:21 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:60:35 + --> $DIR/redundant_field_names.rs:59:35 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:62:32 + --> $DIR/redundant_field_names.rs:61:32 | LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:86:25 + --> $DIR/redundant_field_names.rs:83:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index 42110dbe81e8..4c5846fe837e 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -56,14 +55,12 @@ impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - static V: &'static u8 = &16; } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - static V: &u8 = &17; } diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index bc5200bc8625..64a66be1a83c 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -56,14 +55,12 @@ impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - static V: &'static u8 = &16; } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - static V: &'static u8 = &17; } diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 735113460d28..0938ebf783ff 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -1,5 +1,5 @@ error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:9:17 + --> $DIR/redundant_static_lifetimes.rs:8:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` @@ -7,97 +7,97 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:13:21 + --> $DIR/redundant_static_lifetimes.rs:12:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:15:32 + --> $DIR/redundant_static_lifetimes.rs:14:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:15:47 + --> $DIR/redundant_static_lifetimes.rs:14:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:17:17 + --> $DIR/redundant_static_lifetimes.rs:16:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:19:20 + --> $DIR/redundant_static_lifetimes.rs:18:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:21:19 + --> $DIR/redundant_static_lifetimes.rs:20:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:23:19 + --> $DIR/redundant_static_lifetimes.rs:22:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:25:19 + --> $DIR/redundant_static_lifetimes.rs:24:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:27:25 + --> $DIR/redundant_static_lifetimes.rs:26:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:31:29 + --> $DIR/redundant_static_lifetimes.rs:30:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:33:25 + --> $DIR/redundant_static_lifetimes.rs:32:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:35:28 + --> $DIR/redundant_static_lifetimes.rs:34:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:37:27 + --> $DIR/redundant_static_lifetimes.rs:36:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:39:27 + --> $DIR/redundant_static_lifetimes.rs:38:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:41:27 + --> $DIR/redundant_static_lifetimes.rs:40:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:68:16 + --> $DIR/redundant_static_lifetimes.rs:65:16 | LL | static V: &'static u8 = &17; | -^^^^^^^--- help: consider removing `'static`: `&u8` diff --git a/tests/ui/seek_from_current.fixed b/tests/ui/seek_from_current.fixed index 4b5303324bc6..1309c91b81c9 100644 --- a/tests/ui/seek_from_current.fixed +++ b/tests/ui/seek_from_current.fixed @@ -1,12 +1,11 @@ // run-rustfix #![warn(clippy::seek_from_current)] -#![feature(custom_inner_attributes)] use std::fs::File; use std::io::{self, Seek, SeekFrom, Write}; +#[clippy::msrv = "1.50"] fn _msrv_1_50() -> io::Result<()> { - #![clippy::msrv = "1.50"] let mut f = File::create("foo.txt")?; f.write_all(b"Hi!")?; f.seek(SeekFrom::Current(0))?; @@ -14,8 +13,8 @@ fn _msrv_1_50() -> io::Result<()> { Ok(()) } +#[clippy::msrv = "1.51"] fn _msrv_1_51() -> io::Result<()> { - #![clippy::msrv = "1.51"] let mut f = File::create("foo.txt")?; f.write_all(b"Hi!")?; f.stream_position()?; diff --git a/tests/ui/seek_from_current.rs b/tests/ui/seek_from_current.rs index f93639261a18..5d9b1424cf68 100644 --- a/tests/ui/seek_from_current.rs +++ b/tests/ui/seek_from_current.rs @@ -1,12 +1,11 @@ // run-rustfix #![warn(clippy::seek_from_current)] -#![feature(custom_inner_attributes)] use std::fs::File; use std::io::{self, Seek, SeekFrom, Write}; +#[clippy::msrv = "1.50"] fn _msrv_1_50() -> io::Result<()> { - #![clippy::msrv = "1.50"] let mut f = File::create("foo.txt")?; f.write_all(b"Hi!")?; f.seek(SeekFrom::Current(0))?; @@ -14,8 +13,8 @@ fn _msrv_1_50() -> io::Result<()> { Ok(()) } +#[clippy::msrv = "1.51"] fn _msrv_1_51() -> io::Result<()> { - #![clippy::msrv = "1.51"] let mut f = File::create("foo.txt")?; f.write_all(b"Hi!")?; f.seek(SeekFrom::Current(0))?; diff --git a/tests/ui/seek_from_current.stderr b/tests/ui/seek_from_current.stderr index db1125b53cdf..c079f3611929 100644 --- a/tests/ui/seek_from_current.stderr +++ b/tests/ui/seek_from_current.stderr @@ -1,5 +1,5 @@ error: using `SeekFrom::Current` to start from current position - --> $DIR/seek_from_current.rs:21:5 + --> $DIR/seek_from_current.rs:20:5 | LL | f.seek(SeekFrom::Current(0))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `f.stream_position()` diff --git a/tests/ui/seek_to_start_instead_of_rewind.fixed b/tests/ui/seek_to_start_instead_of_rewind.fixed index 464b6cdef639..9d0d1124c460 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.fixed +++ b/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -1,6 +1,5 @@ // run-rustfix #![allow(unused)] -#![feature(custom_inner_attributes)] #![warn(clippy::seek_to_start_instead_of_rewind)] use std::fs::OpenOptions; @@ -94,9 +93,8 @@ fn main() { assert_eq!(&buf, hello); } +#[clippy::msrv = "1.54"] fn msrv_1_54() { - #![clippy::msrv = "1.54"] - let mut f = OpenOptions::new() .write(true) .read(true) @@ -115,9 +113,8 @@ fn msrv_1_54() { assert_eq!(&buf, hello); } +#[clippy::msrv = "1.55"] fn msrv_1_55() { - #![clippy::msrv = "1.55"] - let mut f = OpenOptions::new() .write(true) .read(true) diff --git a/tests/ui/seek_to_start_instead_of_rewind.rs b/tests/ui/seek_to_start_instead_of_rewind.rs index 68e09bd7c1f0..c5bc57cc3a74 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.rs +++ b/tests/ui/seek_to_start_instead_of_rewind.rs @@ -1,6 +1,5 @@ // run-rustfix #![allow(unused)] -#![feature(custom_inner_attributes)] #![warn(clippy::seek_to_start_instead_of_rewind)] use std::fs::OpenOptions; @@ -94,9 +93,8 @@ fn main() { assert_eq!(&buf, hello); } +#[clippy::msrv = "1.54"] fn msrv_1_54() { - #![clippy::msrv = "1.54"] - let mut f = OpenOptions::new() .write(true) .read(true) @@ -115,9 +113,8 @@ fn msrv_1_54() { assert_eq!(&buf, hello); } +#[clippy::msrv = "1.55"] fn msrv_1_55() { - #![clippy::msrv = "1.55"] - let mut f = OpenOptions::new() .write(true) .read(true) diff --git a/tests/ui/seek_to_start_instead_of_rewind.stderr b/tests/ui/seek_to_start_instead_of_rewind.stderr index de0eec5d909c..6cce025359fe 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.stderr +++ b/tests/ui/seek_to_start_instead_of_rewind.stderr @@ -1,5 +1,5 @@ error: used `seek` to go to the start of the stream - --> $DIR/seek_to_start_instead_of_rewind.rs:54:7 + --> $DIR/seek_to_start_instead_of_rewind.rs:53:7 | LL | t.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` @@ -7,13 +7,13 @@ LL | t.seek(SeekFrom::Start(0)); = note: `-D clippy::seek-to-start-instead-of-rewind` implied by `-D warnings` error: used `seek` to go to the start of the stream - --> $DIR/seek_to_start_instead_of_rewind.rs:59:7 + --> $DIR/seek_to_start_instead_of_rewind.rs:58:7 | LL | t.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` error: used `seek` to go to the start of the stream - --> $DIR/seek_to_start_instead_of_rewind.rs:131:7 + --> $DIR/seek_to_start_instead_of_rewind.rs:128:7 | LL | f.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index e5fe9133f975..074dae5fb286 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::transmute_ptr_to_ref)] #![allow(clippy::match_single_binding)] @@ -51,8 +50,8 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } } +#[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.38"] let a = 0u32; let a = &a as *const u32; let _: &u32 = &*a; @@ -63,8 +62,8 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +#[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.37"] let a = 0u32; let a = &a as *const u32; let _: &u32 = &*a; diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index fe49cdc324fd..2edc122cf471 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::transmute_ptr_to_ref)] #![allow(clippy::match_single_binding)] @@ -51,8 +50,8 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } } +#[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.38"] let a = 0u32; let a = &a as *const u32; let _: &u32 = std::mem::transmute(a); @@ -63,8 +62,8 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +#[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.37"] let a = 0u32; let a = &a as *const u32; let _: &u32 = std::mem::transmute(a); diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 10117ee9182a..b3e6c09d2d7a 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -1,5 +1,5 @@ error: transmute from a pointer type (`*const T`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:8:17 + --> $DIR/transmute_ptr_to_ref.rs:7:17 | LL | let _: &T = std::mem::transmute(p); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` @@ -7,127 +7,127 @@ LL | let _: &T = std::mem::transmute(p); = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:11:21 + --> $DIR/transmute_ptr_to_ref.rs:10:21 | LL | let _: &mut T = std::mem::transmute(m); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` error: transmute from a pointer type (`*mut T`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:14:17 + --> $DIR/transmute_ptr_to_ref.rs:13:17 | LL | let _: &T = std::mem::transmute(m); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:17:21 + --> $DIR/transmute_ptr_to_ref.rs:16:21 | LL | let _: &mut T = std::mem::transmute(p as *mut T); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` error: transmute from a pointer type (`*const U`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:20:17 + --> $DIR/transmute_ptr_to_ref.rs:19:17 | LL | let _: &T = std::mem::transmute(o); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:23:21 + --> $DIR/transmute_ptr_to_ref.rs:22:21 | LL | let _: &mut T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:26:17 + --> $DIR/transmute_ptr_to_ref.rs:25:17 | LL | let _: &T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) - --> $DIR/transmute_ptr_to_ref.rs:36:32 + --> $DIR/transmute_ptr_to_ref.rs:35:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) - --> $DIR/transmute_ptr_to_ref.rs:38:33 + --> $DIR/transmute_ptr_to_ref.rs:37:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) - --> $DIR/transmute_ptr_to_ref.rs:42:14 + --> $DIR/transmute_ptr_to_ref.rs:41:14 | LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:47:14 + --> $DIR/transmute_ptr_to_ref.rs:46:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:48:14 + --> $DIR/transmute_ptr_to_ref.rs:47:14 | LL | 1 => std::mem::transmute(y), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:49:14 + --> $DIR/transmute_ptr_to_ref.rs:48:14 | LL | 2 => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:50:14 + --> $DIR/transmute_ptr_to_ref.rs:49:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(y), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:58:19 + --> $DIR/transmute_ptr_to_ref.rs:57:19 | LL | let _: &u32 = std::mem::transmute(a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:59:19 + --> $DIR/transmute_ptr_to_ref.rs:58:19 | LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:61:14 + --> $DIR/transmute_ptr_to_ref.rs:60:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:62:14 + --> $DIR/transmute_ptr_to_ref.rs:61:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:70:19 + --> $DIR/transmute_ptr_to_ref.rs:69:19 | LL | let _: &u32 = std::mem::transmute(a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:71:19 + --> $DIR/transmute_ptr_to_ref.rs:70:19 | LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:73:14 + --> $DIR/transmute_ptr_to_ref.rs:72:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:74:14 + --> $DIR/transmute_ptr_to_ref.rs:73:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index ca56c95c23f4..46ec7771114f 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -1,6 +1,5 @@ // aux-build:proc_macro_with_span.rs // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::uninlined_format_args)] #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] @@ -164,14 +163,14 @@ fn main() { tester(42); } +#[clippy::msrv = "1.57"] fn _under_msrv() { - #![clippy::msrv = "1.57"] let local_i32 = 1; println!("don't expand='{}'", local_i32); } +#[clippy::msrv = "1.58"] fn _meets_msrv() { - #![clippy::msrv = "1.58"] let local_i32 = 1; println!("expand='{local_i32}'"); } diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs index 8e495ebd083a..35b3677a8968 100644 --- a/tests/ui/uninlined_format_args.rs +++ b/tests/ui/uninlined_format_args.rs @@ -1,6 +1,5 @@ // aux-build:proc_macro_with_span.rs // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::uninlined_format_args)] #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] @@ -169,14 +168,14 @@ fn main() { tester(42); } +#[clippy::msrv = "1.57"] fn _under_msrv() { - #![clippy::msrv = "1.57"] let local_i32 = 1; println!("don't expand='{}'", local_i32); } +#[clippy::msrv = "1.58"] fn _meets_msrv() { - #![clippy::msrv = "1.58"] let local_i32 = 1; println!("expand='{}'", local_i32); } diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index 1182d57ce9b7..05ed5b6616c0 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -1,5 +1,5 @@ error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:41:5 + --> $DIR/uninlined_format_args.rs:40:5 | LL | println!("val='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:42:5 + --> $DIR/uninlined_format_args.rs:41:5 | LL | println!("val='{ }'", local_i32); // 3 spaces | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL + println!("val='{local_i32}'"); // 3 spaces | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:43:5 + --> $DIR/uninlined_format_args.rs:42:5 | LL | println!("val='{ }'", local_i32); // tab | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL + println!("val='{local_i32}'"); // tab | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:44:5 + --> $DIR/uninlined_format_args.rs:43:5 | LL | println!("val='{ }'", local_i32); // space+tab | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + println!("val='{local_i32}'"); // space+tab | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:45:5 + --> $DIR/uninlined_format_args.rs:44:5 | LL | println!("val='{ }'", local_i32); // tab+space | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL + println!("val='{local_i32}'"); // tab+space | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:46:5 + --> $DIR/uninlined_format_args.rs:45:5 | LL | / println!( LL | | "val='{ @@ -70,7 +70,7 @@ LL | | ); | |_____^ error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:51:5 + --> $DIR/uninlined_format_args.rs:50:5 | LL | println!("{}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -82,7 +82,7 @@ LL + println!("{local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:52:5 + --> $DIR/uninlined_format_args.rs:51:5 | LL | println!("{}", fn_arg); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +94,7 @@ LL + println!("{fn_arg}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:53:5 + --> $DIR/uninlined_format_args.rs:52:5 | LL | println!("{:?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +106,7 @@ LL + println!("{local_i32:?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:54:5 + --> $DIR/uninlined_format_args.rs:53:5 | LL | println!("{:#?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL + println!("{local_i32:#?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:55:5 + --> $DIR/uninlined_format_args.rs:54:5 | LL | println!("{:4}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -130,7 +130,7 @@ LL + println!("{local_i32:4}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:56:5 + --> $DIR/uninlined_format_args.rs:55:5 | LL | println!("{:04}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -142,7 +142,7 @@ LL + println!("{local_i32:04}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:57:5 + --> $DIR/uninlined_format_args.rs:56:5 | LL | println!("{:<3}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL + println!("{local_i32:<3}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:58:5 + --> $DIR/uninlined_format_args.rs:57:5 | LL | println!("{:#010x}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL + println!("{local_i32:#010x}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:59:5 + --> $DIR/uninlined_format_args.rs:58:5 | LL | println!("{:.1}", local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -178,7 +178,7 @@ LL + println!("{local_f64:.1}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:60:5 + --> $DIR/uninlined_format_args.rs:59:5 | LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -190,7 +190,7 @@ LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:61:5 + --> $DIR/uninlined_format_args.rs:60:5 | LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -202,7 +202,7 @@ LL + println!("Hello {local_i32} is {local_f64:.*}", 5); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:62:5 + --> $DIR/uninlined_format_args.rs:61:5 | LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -214,7 +214,7 @@ LL + println!("Hello {local_i32} is {local_f64:.*}", 5); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:63:5 + --> $DIR/uninlined_format_args.rs:62:5 | LL | println!("{} {}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -226,7 +226,7 @@ LL + println!("{local_i32} {local_f64}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:64:5 + --> $DIR/uninlined_format_args.rs:63:5 | LL | println!("{}, {}", local_i32, local_opt.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -238,7 +238,7 @@ LL + println!("{local_i32}, {}", local_opt.unwrap()); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:65:5 + --> $DIR/uninlined_format_args.rs:64:5 | LL | println!("{}", val); | ^^^^^^^^^^^^^^^^^^^ @@ -250,7 +250,7 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:66:5 + --> $DIR/uninlined_format_args.rs:65:5 | LL | println!("{}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -262,7 +262,7 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:68:5 + --> $DIR/uninlined_format_args.rs:67:5 | LL | println!("val='{/t }'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -274,7 +274,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:69:5 + --> $DIR/uninlined_format_args.rs:68:5 | LL | println!("val='{/n }'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -286,7 +286,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:70:5 + --> $DIR/uninlined_format_args.rs:69:5 | LL | println!("val='{local_i32}'", local_i32 = local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -298,7 +298,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:71:5 + --> $DIR/uninlined_format_args.rs:70:5 | LL | println!("val='{local_i32}'", local_i32 = fn_arg); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -310,7 +310,7 @@ LL + println!("val='{fn_arg}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:72:5 + --> $DIR/uninlined_format_args.rs:71:5 | LL | println!("{0}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -322,7 +322,7 @@ LL + println!("{local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:73:5 + --> $DIR/uninlined_format_args.rs:72:5 | LL | println!("{0:?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -334,7 +334,7 @@ LL + println!("{local_i32:?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:74:5 + --> $DIR/uninlined_format_args.rs:73:5 | LL | println!("{0:#?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -346,7 +346,7 @@ LL + println!("{local_i32:#?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:75:5 + --> $DIR/uninlined_format_args.rs:74:5 | LL | println!("{0:04}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -358,7 +358,7 @@ LL + println!("{local_i32:04}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:76:5 + --> $DIR/uninlined_format_args.rs:75:5 | LL | println!("{0:<3}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -370,7 +370,7 @@ LL + println!("{local_i32:<3}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:77:5 + --> $DIR/uninlined_format_args.rs:76:5 | LL | println!("{0:#010x}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -382,7 +382,7 @@ LL + println!("{local_i32:#010x}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:78:5 + --> $DIR/uninlined_format_args.rs:77:5 | LL | println!("{0:.1}", local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -394,7 +394,7 @@ LL + println!("{local_f64:.1}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:79:5 + --> $DIR/uninlined_format_args.rs:78:5 | LL | println!("{0} {0}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -406,7 +406,7 @@ LL + println!("{local_i32} {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:80:5 + --> $DIR/uninlined_format_args.rs:79:5 | LL | println!("{1} {} {0} {}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -418,7 +418,7 @@ LL + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:81:5 + --> $DIR/uninlined_format_args.rs:80:5 | LL | println!("{0} {1}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -430,7 +430,7 @@ LL + println!("{local_i32} {local_f64}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:82:5 + --> $DIR/uninlined_format_args.rs:81:5 | LL | println!("{1} {0}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -442,7 +442,7 @@ LL + println!("{local_f64} {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:83:5 + --> $DIR/uninlined_format_args.rs:82:5 | LL | println!("{1} {0} {1} {0}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -454,7 +454,7 @@ LL + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:85:5 + --> $DIR/uninlined_format_args.rs:84:5 | LL | println!("{v}", v = local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -466,7 +466,7 @@ LL + println!("{local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:86:5 + --> $DIR/uninlined_format_args.rs:85:5 | LL | println!("{local_i32:0$}", width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -478,7 +478,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:87:5 + --> $DIR/uninlined_format_args.rs:86:5 | LL | println!("{local_i32:w$}", w = width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -490,7 +490,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:88:5 + --> $DIR/uninlined_format_args.rs:87:5 | LL | println!("{local_i32:.0$}", prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -502,7 +502,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:89:5 + --> $DIR/uninlined_format_args.rs:88:5 | LL | println!("{local_i32:.p$}", p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -514,7 +514,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:90:5 + --> $DIR/uninlined_format_args.rs:89:5 | LL | println!("{:0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -526,7 +526,7 @@ LL + println!("{val:val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:91:5 + --> $DIR/uninlined_format_args.rs:90:5 | LL | println!("{0:0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -538,7 +538,7 @@ LL + println!("{val:val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:92:5 + --> $DIR/uninlined_format_args.rs:91:5 | LL | println!("{:0$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -550,7 +550,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:93:5 + --> $DIR/uninlined_format_args.rs:92:5 | LL | println!("{0:0$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -562,7 +562,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:94:5 + --> $DIR/uninlined_format_args.rs:93:5 | LL | println!("{0:0$.v$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -574,7 +574,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:95:5 + --> $DIR/uninlined_format_args.rs:94:5 | LL | println!("{0:v$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -586,7 +586,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:96:5 + --> $DIR/uninlined_format_args.rs:95:5 | LL | println!("{v:0$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -598,7 +598,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:97:5 + --> $DIR/uninlined_format_args.rs:96:5 | LL | println!("{v:v$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -610,7 +610,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:98:5 + --> $DIR/uninlined_format_args.rs:97:5 | LL | println!("{v:0$.v$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -622,7 +622,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:99:5 + --> $DIR/uninlined_format_args.rs:98:5 | LL | println!("{v:v$.v$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -634,7 +634,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:100:5 + --> $DIR/uninlined_format_args.rs:99:5 | LL | println!("{:0$}", width); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -646,7 +646,7 @@ LL + println!("{width:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:101:5 + --> $DIR/uninlined_format_args.rs:100:5 | LL | println!("{:1$}", local_i32, width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -658,7 +658,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:102:5 + --> $DIR/uninlined_format_args.rs:101:5 | LL | println!("{:w$}", w = width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -670,7 +670,7 @@ LL + println!("{width:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:103:5 + --> $DIR/uninlined_format_args.rs:102:5 | LL | println!("{:w$}", local_i32, w = width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -682,7 +682,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:104:5 + --> $DIR/uninlined_format_args.rs:103:5 | LL | println!("{:.0$}", prec); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -694,7 +694,7 @@ LL + println!("{prec:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:105:5 + --> $DIR/uninlined_format_args.rs:104:5 | LL | println!("{:.1$}", local_i32, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -706,7 +706,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:106:5 + --> $DIR/uninlined_format_args.rs:105:5 | LL | println!("{:.p$}", p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -718,7 +718,7 @@ LL + println!("{prec:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:107:5 + --> $DIR/uninlined_format_args.rs:106:5 | LL | println!("{:.p$}", local_i32, p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -730,7 +730,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:108:5 + --> $DIR/uninlined_format_args.rs:107:5 | LL | println!("{:0$.1$}", width, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -742,7 +742,7 @@ LL + println!("{width:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:109:5 + --> $DIR/uninlined_format_args.rs:108:5 | LL | println!("{:0$.w$}", width, w = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -754,7 +754,7 @@ LL + println!("{width:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:110:5 + --> $DIR/uninlined_format_args.rs:109:5 | LL | println!("{:1$.2$}", local_f64, width, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -766,7 +766,7 @@ LL + println!("{local_f64:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:111:5 + --> $DIR/uninlined_format_args.rs:110:5 | LL | println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -778,7 +778,7 @@ LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:112:5 + --> $DIR/uninlined_format_args.rs:111:5 | LL | / println!( LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", @@ -787,7 +787,7 @@ LL | | ); | |_____^ error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:123:5 + --> $DIR/uninlined_format_args.rs:122:5 | LL | println!("Width = {}, value with width = {:0$}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -799,7 +799,7 @@ LL + println!("Width = {local_i32}, value with width = {local_f64:local_i32$ | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:124:5 + --> $DIR/uninlined_format_args.rs:123:5 | LL | println!("{:w$.p$}", local_i32, w = width, p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -811,7 +811,7 @@ LL + println!("{local_i32:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:125:5 + --> $DIR/uninlined_format_args.rs:124:5 | LL | println!("{:w$.p$}", w = width, p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -823,7 +823,7 @@ LL + println!("{width:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:126:20 + --> $DIR/uninlined_format_args.rs:125:20 | LL | println!("{}", format!("{}", local_i32)); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -835,7 +835,7 @@ LL + println!("{}", format!("{local_i32}")); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:144:5 + --> $DIR/uninlined_format_args.rs:143:5 | LL | / println!( LL | | "{}", @@ -845,7 +845,7 @@ LL | | ); | |_____^ error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:149:5 + --> $DIR/uninlined_format_args.rs:148:5 | LL | println!("{}", /* comment with a comma , in it */ val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -857,7 +857,7 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:155:9 + --> $DIR/uninlined_format_args.rs:154:9 | LL | panic!("p1 {}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -869,7 +869,7 @@ LL + panic!("p1 {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:158:9 + --> $DIR/uninlined_format_args.rs:157:9 | LL | panic!("p2 {0}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -881,7 +881,7 @@ LL + panic!("p2 {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:161:9 + --> $DIR/uninlined_format_args.rs:160:9 | LL | panic!("p3 {local_i32}", local_i32 = local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -893,7 +893,7 @@ LL + panic!("p3 {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:181:5 + --> $DIR/uninlined_format_args.rs:180:5 | LL | println!("expand='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index fadcf7f9c9e8..ddeda795f817 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -2,7 +2,6 @@ #![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] -#![feature(custom_inner_attributes)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -215,14 +214,14 @@ fn get_file_path(_file_type: &FileType) -> Result Result $DIR/unnecessary_to_owned.rs:151:64 + --> $DIR/unnecessary_to_owned.rs:150:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:151:20 + --> $DIR/unnecessary_to_owned.rs:150:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::redundant-clone` implied by `-D warnings` error: redundant clone - --> $DIR/unnecessary_to_owned.rs:152:40 + --> $DIR/unnecessary_to_owned.rs:151:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:152:21 + --> $DIR/unnecessary_to_owned.rs:151:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:153:48 + --> $DIR/unnecessary_to_owned.rs:152:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:153:19 + --> $DIR/unnecessary_to_owned.rs:152:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:154:35 + --> $DIR/unnecessary_to_owned.rs:153:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:154:18 + --> $DIR/unnecessary_to_owned.rs:153:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:155:39 + --> $DIR/unnecessary_to_owned.rs:154:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:155:20 + --> $DIR/unnecessary_to_owned.rs:154:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:60:36 + --> $DIR/unnecessary_to_owned.rs:59:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -68,415 +68,415 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:61:19 + --> $DIR/unnecessary_to_owned.rs:60:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> $DIR/unnecessary_to_owned.rs:63:20 + --> $DIR/unnecessary_to_owned.rs:62:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:64:38 + --> $DIR/unnecessary_to_owned.rs:63:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:65:20 + --> $DIR/unnecessary_to_owned.rs:64:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> $DIR/unnecessary_to_owned.rs:67:18 + --> $DIR/unnecessary_to_owned.rs:66:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:68:34 + --> $DIR/unnecessary_to_owned.rs:67:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:69:18 + --> $DIR/unnecessary_to_owned.rs:68:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:71:17 + --> $DIR/unnecessary_to_owned.rs:70:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:72:30 + --> $DIR/unnecessary_to_owned.rs:71:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:73:17 + --> $DIR/unnecessary_to_owned.rs:72:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:74:17 + --> $DIR/unnecessary_to_owned.rs:73:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:76:19 + --> $DIR/unnecessary_to_owned.rs:75:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:77:36 + --> $DIR/unnecessary_to_owned.rs:76:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:78:19 + --> $DIR/unnecessary_to_owned.rs:77:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:79:19 + --> $DIR/unnecessary_to_owned.rs:78:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:80:19 + --> $DIR/unnecessary_to_owned.rs:79:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:83:42 + --> $DIR/unnecessary_to_owned.rs:82:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:86:25 + --> $DIR/unnecessary_to_owned.rs:85:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:87:26 + --> $DIR/unnecessary_to_owned.rs:86:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:88:24 + --> $DIR/unnecessary_to_owned.rs:87:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:89:23 + --> $DIR/unnecessary_to_owned.rs:88:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:90:25 + --> $DIR/unnecessary_to_owned.rs:89:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:92:30 + --> $DIR/unnecessary_to_owned.rs:91:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:93:31 + --> $DIR/unnecessary_to_owned.rs:92:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:94:29 + --> $DIR/unnecessary_to_owned.rs:93:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:95:28 + --> $DIR/unnecessary_to_owned.rs:94:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:96:30 + --> $DIR/unnecessary_to_owned.rs:95:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:29 + --> $DIR/unnecessary_to_owned.rs:97:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:43 + --> $DIR/unnecessary_to_owned.rs:97:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:99:29 + --> $DIR/unnecessary_to_owned.rs:98:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:99:47 + --> $DIR/unnecessary_to_owned.rs:98:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:101:26 + --> $DIR/unnecessary_to_owned.rs:100:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:102:27 + --> $DIR/unnecessary_to_owned.rs:101:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:103:25 + --> $DIR/unnecessary_to_owned.rs:102:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:104:24 + --> $DIR/unnecessary_to_owned.rs:103:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:105:24 + --> $DIR/unnecessary_to_owned.rs:104:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:106:26 + --> $DIR/unnecessary_to_owned.rs:105:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:107:26 + --> $DIR/unnecessary_to_owned.rs:106:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:108:26 + --> $DIR/unnecessary_to_owned.rs:107:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:110:31 + --> $DIR/unnecessary_to_owned.rs:109:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:111:32 + --> $DIR/unnecessary_to_owned.rs:110:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:112:30 + --> $DIR/unnecessary_to_owned.rs:111:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:113:29 + --> $DIR/unnecessary_to_owned.rs:112:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:114:29 + --> $DIR/unnecessary_to_owned.rs:113:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:115:31 + --> $DIR/unnecessary_to_owned.rs:114:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:116:31 + --> $DIR/unnecessary_to_owned.rs:115:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:117:31 + --> $DIR/unnecessary_to_owned.rs:116:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:30 + --> $DIR/unnecessary_to_owned.rs:118:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:44 + --> $DIR/unnecessary_to_owned.rs:118:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:30 + --> $DIR/unnecessary_to_owned.rs:119:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:44 + --> $DIR/unnecessary_to_owned.rs:119:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:30 + --> $DIR/unnecessary_to_owned.rs:120:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:44 + --> $DIR/unnecessary_to_owned.rs:120:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:30 + --> $DIR/unnecessary_to_owned.rs:121:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:48 + --> $DIR/unnecessary_to_owned.rs:121:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:30 + --> $DIR/unnecessary_to_owned.rs:122:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:52 + --> $DIR/unnecessary_to_owned.rs:122:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:124:30 + --> $DIR/unnecessary_to_owned.rs:123:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:124:48 + --> $DIR/unnecessary_to_owned.rs:123:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:126:20 + --> $DIR/unnecessary_to_owned.rs:125:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:128:13 + --> $DIR/unnecessary_to_owned.rs:127:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:129:13 + --> $DIR/unnecessary_to_owned.rs:128:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:130:13 + --> $DIR/unnecessary_to_owned.rs:129:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:131:13 + --> $DIR/unnecessary_to_owned.rs:130:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:133:13 + --> $DIR/unnecessary_to_owned.rs:132:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:134:13 + --> $DIR/unnecessary_to_owned.rs:133:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:135:13 + --> $DIR/unnecessary_to_owned.rs:134:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:136:13 + --> $DIR/unnecessary_to_owned.rs:135:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:198:14 + --> $DIR/unnecessary_to_owned.rs:197:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -492,25 +492,25 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:221:14 + --> $DIR/unnecessary_to_owned.rs:220:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:226:14 + --> $DIR/unnecessary_to_owned.rs:225:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:273:24 + --> $DIR/unnecessary_to_owned.rs:272:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:381:12 + --> $DIR/unnecessary_to_owned.rs:380:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index 9786c7b12128..0a8e7b34dfa4 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![feature(box_patterns, custom_inner_attributes)] +#![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] @@ -34,14 +34,12 @@ fn main() { if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } +#[clippy::msrv = "1.52"] fn msrv_1_52() { - #![clippy::msrv = "1.52"] - if let [1] | [52] = [0] {} } +#[clippy::msrv = "1.53"] fn msrv_1_53() { - #![clippy::msrv = "1.53"] - if let [1 | 53] = [0] {} } diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index f57322396d4a..2c454adfe89d 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -1,6 +1,6 @@ // run-rustfix -#![feature(box_patterns, custom_inner_attributes)] +#![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] @@ -34,14 +34,12 @@ fn main() { if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } +#[clippy::msrv = "1.52"] fn msrv_1_52() { - #![clippy::msrv = "1.52"] - if let [1] | [52] = [0] {} } +#[clippy::msrv = "1.53"] fn msrv_1_53() { - #![clippy::msrv = "1.53"] - if let [1] | [53] = [0] {} } diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index fbc12fff0b0e..a1f193db555a 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -176,7 +176,7 @@ LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | ~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:46:12 + --> $DIR/unnested_or_patterns.rs:44:12 | LL | if let [1] | [53] = [0] {} | ^^^^^^^^^^ diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 3b54fe9d5ff3..0a6166571ebe 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -1,7 +1,6 @@ // run-rustfix // aux-build:proc_macro_derive.rs -#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -619,9 +618,8 @@ mod issue6902 { } } +#[clippy::msrv = "1.36"] fn msrv_1_36() { - #![clippy::msrv = "1.36"] - enum E { A, } @@ -635,9 +633,8 @@ fn msrv_1_36() { } } +#[clippy::msrv = "1.37"] fn msrv_1_37() { - #![clippy::msrv = "1.37"] - enum E { A, } diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index bf87633cd2d8..39c2b431f7fb 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -1,7 +1,6 @@ // run-rustfix // aux-build:proc_macro_derive.rs -#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -619,9 +618,8 @@ mod issue6902 { } } +#[clippy::msrv = "1.36"] fn msrv_1_36() { - #![clippy::msrv = "1.36"] - enum E { A, } @@ -635,9 +633,8 @@ fn msrv_1_36() { } } +#[clippy::msrv = "1.37"] fn msrv_1_37() { - #![clippy::msrv = "1.37"] - enum E { A, } diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 16fb0609242c..48364c40c3b2 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:21 + --> $DIR/use_self.rs:22:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -7,247 +7,247 @@ LL | fn new() -> Foo { = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:24:13 + --> $DIR/use_self.rs:23:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:26:22 + --> $DIR/use_self.rs:25:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:27:13 + --> $DIR/use_self.rs:26:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:32:25 + --> $DIR/use_self.rs:31:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:33:13 + --> $DIR/use_self.rs:32:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:98:24 + --> $DIR/use_self.rs:97:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:98:55 + --> $DIR/use_self.rs:97:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:113:13 + --> $DIR/use_self.rs:112:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:29 + --> $DIR/use_self.rs:147:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:149:21 + --> $DIR/use_self.rs:148:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:160:21 + --> $DIR/use_self.rs:159:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:161:13 + --> $DIR/use_self.rs:160:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:178:21 + --> $DIR/use_self.rs:177:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:179:21 + --> $DIR/use_self.rs:178:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:180:21 + --> $DIR/use_self.rs:179:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:222:13 + --> $DIR/use_self.rs:221:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:223:13 + --> $DIR/use_self.rs:222:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:225:13 + --> $DIR/use_self.rs:224:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:244:13 + --> $DIR/use_self.rs:243:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:25 + --> $DIR/use_self.rs:257:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:259:13 + --> $DIR/use_self.rs:258:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:263:16 + --> $DIR/use_self.rs:262:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:263:22 + --> $DIR/use_self.rs:262:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:286:29 + --> $DIR/use_self.rs:285:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:287:13 + --> $DIR/use_self.rs:286:13 | LL | Foo:: { value } | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:459:13 + --> $DIR/use_self.rs:458:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:496:13 + --> $DIR/use_self.rs:495:13 | LL | S2::new() | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:533:17 + --> $DIR/use_self.rs:532:17 | LL | Foo::Bar => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:534:17 + --> $DIR/use_self.rs:533:17 | LL | Foo::Baz => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:540:20 + --> $DIR/use_self.rs:539:20 | LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:564:17 + --> $DIR/use_self.rs:563:17 | LL | Something::Num(n) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:565:17 + --> $DIR/use_self.rs:564:17 | LL | Something::TupleNums(n, _m) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:566:17 + --> $DIR/use_self.rs:565:17 | LL | Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:572:17 + --> $DIR/use_self.rs:571:17 | LL | crate::issue8845::Something::Num(n) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:573:17 + --> $DIR/use_self.rs:572:17 | LL | crate::issue8845::Something::TupleNums(n, _m) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:574:17 + --> $DIR/use_self.rs:573:17 | LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:590:17 + --> $DIR/use_self.rs:589:17 | LL | let Foo(x) = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:595:17 + --> $DIR/use_self.rs:594:17 | LL | let crate::issue8845::Foo(x) = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:602:17 + --> $DIR/use_self.rs:601:17 | LL | let Bar { x, .. } = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:607:17 + --> $DIR/use_self.rs:606:17 | LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:648:17 + --> $DIR/use_self.rs:645:17 | LL | E::A => {}, | ^ help: use the applicable keyword: `Self` From 8c50dfb54649cbd67e37e7a456e736241740666d Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 27 Nov 2022 22:01:21 +0900 Subject: [PATCH 274/524] Remove blank lines when needless_return returns no value fix https://github.com/rust-lang/rust-clippy/issues/9416 --- clippy_lints/src/returns.rs | 30 +++++++++--- tests/ui/needless_return.fixed | 19 ++++---- tests/ui/needless_return.rs | 13 +++++ tests/ui/needless_return.stderr | 85 +++++++++++++++++++++++---------- 4 files changed, 107 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2b2a41d16011..933258f9c3ed 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -12,6 +12,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::BytePos; declare_clippy_lint! { /// ### What it does @@ -209,13 +210,14 @@ fn check_final_expr<'tcx>( if cx.tcx.hir().attrs(expr.hir_id).is_empty() { let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); if !borrows { - emit_return_lint( - cx, - peeled_drop_expr.span, - semi_spans, - inner.as_ref().map(|i| i.span), - replacement, - ); + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement); } } }, @@ -289,3 +291,17 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) }) .is_some() } + +// Go backwards while encountering whitespace and extend the given Span to that point. +#[expect(clippy::cast_possible_truncation)] +fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { + if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { + let ws = [' ', '\t', '\n']; + if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) { + let len = prev_source.len() - non_ws_pos - 1; + return sp.with_lo(BytePos(sp.lo().0 - len as u32)); + } + } + + sp +} diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d2163b14fcad..4386aaec49e2 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -59,14 +59,11 @@ fn test_macro_call() -> i32 { } fn test_void_fun() { - } fn test_void_if_fun(b: bool) { if b { - } else { - } } @@ -82,7 +79,6 @@ fn test_nested_match(x: u32) { 0 => (), 1 => { let _ = 42; - }, _ => (), } @@ -126,7 +122,6 @@ mod issue6501 { fn test_closure() { let _ = || { - }; let _ = || {}; } @@ -179,14 +174,11 @@ async fn async_test_macro_call() -> i32 { } async fn async_test_void_fun() { - } async fn async_test_void_if_fun(b: bool) { if b { - } else { - } } @@ -269,4 +261,15 @@ fn issue9503(x: usize) -> isize { } } +mod issue9416 { + pub fn with_newline() { + let _ = 42; + } + + #[rustfmt::skip] + pub fn oneline() { + let _ = 42; + } +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 114414b5fac7..666dc54b76b4 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -269,4 +269,17 @@ fn issue9503(x: usize) -> isize { }; } +mod issue9416 { + pub fn with_newline() { + let _ = 42; + + return; + } + + #[rustfmt::skip] + pub fn oneline() { + let _ = 42; return; + } +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 047fb6c2311a..a8b5d86cd558 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -72,26 +72,32 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:62:5 + --> $DIR/needless_return.rs:61:21 | -LL | return; - | ^^^^^^ +LL | fn test_void_fun() { + | _____________________^ +LL | | return; + | |__________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:67:9 + --> $DIR/needless_return.rs:66:11 | -LL | return; - | ^^^^^^ +LL | if b { + | ___________^ +LL | | return; + | |______________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:69:9 + --> $DIR/needless_return.rs:68:13 | -LL | return; - | ^^^^^^ +LL | } else { + | _____________^ +LL | | return; + | |______________^ | = help: remove `return` @@ -104,10 +110,12 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:85:13 + --> $DIR/needless_return.rs:84:24 | -LL | return; - | ^^^^^^ +LL | let _ = 42; + | ________________________^ +LL | | return; + | |__________________^ | = help: remove `return` @@ -144,10 +152,12 @@ LL | bar.unwrap_or_else(|_| return) = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:129:13 + --> $DIR/needless_return.rs:128:21 | -LL | return; - | ^^^^^^ +LL | let _ = || { + | _____________________^ +LL | | return; + | |__________________^ | = help: remove `return` @@ -240,26 +250,32 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:182:5 + --> $DIR/needless_return.rs:181:33 | -LL | return; - | ^^^^^^ +LL | async fn async_test_void_fun() { + | _________________________________^ +LL | | return; + | |__________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:187:9 + --> $DIR/needless_return.rs:186:11 | -LL | return; - | ^^^^^^ +LL | if b { + | ___________^ +LL | | return; + | |______________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:189:9 + --> $DIR/needless_return.rs:188:13 | -LL | return; - | ^^^^^^ +LL | } else { + | _____________^ +LL | | return; + | |______________^ | = help: remove `return` @@ -351,5 +367,24 @@ LL | return !*(x as *const isize); | = help: remove `return` -error: aborting due to 44 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:274:20 + | +LL | let _ = 42; + | ____________________^ +LL | | +LL | | return; + | |______________^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:281:20 + | +LL | let _ = 42; return; + | ^^^^^^^ + | + = help: remove `return` + +error: aborting due to 46 previous errors From 39d0477080de2c458cdbeb4a0a44d267d58af521 Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 27 Nov 2022 22:41:06 +0900 Subject: [PATCH 275/524] Refactor BytePos handling --- clippy_lints/src/returns.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 933258f9c3ed..81143d7799ea 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -12,7 +12,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::BytePos; +use rustc_span::{BytePos, Pos}; declare_clippy_lint! { /// ### What it does @@ -293,13 +293,12 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) } // Go backwards while encountering whitespace and extend the given Span to that point. -#[expect(clippy::cast_possible_truncation)] fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { let ws = [' ', '\t', '\n']; if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) { let len = prev_source.len() - non_ws_pos - 1; - return sp.with_lo(BytePos(sp.lo().0 - len as u32)); + return sp.with_lo(sp.lo() - BytePos::from_usize(len)); } } From 7a2d92e1f29f774acb640b8e4262aed1b8f7f9a4 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sun, 27 Nov 2022 10:12:51 -0500 Subject: [PATCH 276/524] Add allow-mixed-uninlined-format-args config Implement `allow-mixed-uninlined-format-args` config param to change the behavior of the `uninlined_format_args` lint. Now it is a part of `style`, and won't propose inlining in case of a mixed usage, e.g. `print!("{} {}", var, 1+2)`. If the user sets allow-mixed-uninlined-format-args config param to `false`, then it would behave like before, proposing to inline args even in the mixed case. --- clippy_lints/src/format_args.rs | 62 +- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/utils/conf.rs | 4 + .../auxiliary/proc_macro_with_span.rs | 32 + .../clippy.toml | 1 + .../uninlined_format_args.fixed | 177 ++++ .../uninlined_format_args.rs | 182 ++++ .../uninlined_format_args.stderr | 908 ++++++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 1 + tests/ui/uninlined_format_args.fixed | 8 +- ...lined_format_args_panic.edition2018.stderr | 1 + ...lined_format_args_panic.edition2021.stderr | 1 + 12 files changed, 1359 insertions(+), 21 deletions(-) create mode 100644 tests/ui-toml/allow_mixed_uninlined_format_args/auxiliary/proc_macro_with_span.rs create mode 100644 tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml create mode 100644 tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed create mode 100644 tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs create mode 100644 tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index fd3ce2f3d6cd..40a37d39ecf8 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; -use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; +use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred}; use clippy_utils::macros::{ is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage, }; @@ -106,19 +106,25 @@ declare_clippy_lint! { /// format!("{var:.prec$}"); /// ``` /// - /// ### Known Problems - /// - /// There may be a false positive if the format string is expanded from certain proc macros: - /// - /// ```ignore - /// println!(indoc!("{}"), var); + /// If allow-mixed-uninlined-format-args is set to false in clippy.toml, + /// the following code will also trigger the lint: + /// ```rust + /// # let var = 42; + /// format!("{} {}", var, 1+2); + /// ``` + /// Use instead: + /// ```rust + /// # let var = 42; + /// format!("{var} {}", 1+2); /// ``` /// + /// ### Known Problems + /// /// If a format string contains a numbered argument that cannot be inlined /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.65.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } @@ -162,12 +168,16 @@ impl_lint_pass!(FormatArgs => [ pub struct FormatArgs { msrv: Msrv, + ignore_mixed: bool, } impl FormatArgs { #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { + Self { + msrv, + ignore_mixed: allow_mixed_uninlined_format_args, + } } } @@ -192,7 +202,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { check_to_string_in_format_args(cx, name, arg.param.value); } if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id); + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed); } } } @@ -270,7 +280,13 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { } } -fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) { +fn check_uninlined_args( + cx: &LateContext<'_>, + args: &FormatArgsExpn<'_>, + call_site: Span, + def_id: DefId, + ignore_mixed: bool, +) { if args.format_string.span.from_expansion() { return; } @@ -285,7 +301,7 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si // we cannot remove any other arguments in the format string, // because the index numbers might be wrong after inlining. // Example of an un-inlinable format: print!("{}{1}", foo, 2) - if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() { + if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() { return; } @@ -305,11 +321,23 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si Applicability::MachineApplicable, if multiline_fix { CompletelyHidden } else { ShowCode }, ); + if ignore_mixed { + // Improve lint config discoverability + diag.note_once( + "this lint can also fix mixed format arg inlining if \ + `allow-mixed-uninlined-format-args = false` is set in the `clippy.toml` file", + ); + } }, ); } -fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool { +fn check_one_arg( + args: &FormatArgsExpn<'_>, + param: &FormatParam<'_>, + fixes: &mut Vec<(Span, String)>, + ignore_mixed: bool, +) -> bool { if matches!(param.kind, Implicit | Starred | Named(_) | Numbered) && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind && let [segment] = path.segments @@ -324,8 +352,10 @@ fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut fixes.push((arg_span, String::new())); true // successful inlining, continue checking } else { - // if we can't inline a numbered argument, we can't continue - param.kind != Numbered + // Do not continue inlining (return false) in case + // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)` + // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already + param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_))) } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 17dbd983b6c0..3fe39488ab82 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -828,7 +828,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv()))); + let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args; + store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b37d4239477e..b6dc8cd7ab11 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -402,6 +402,10 @@ define_Conf! { /// A list of paths to types that should be treated like `Arc`, i.e. ignored but /// for the generic parameters for determining interior mutability (ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()])), + /// Lint: UNINLINED_FORMAT_ARGS. + /// + /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` + (allow_mixed_uninlined_format_args: bool = true), } /// Search for the configuration file. diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/auxiliary/proc_macro_with_span.rs b/tests/ui-toml/allow_mixed_uninlined_format_args/auxiliary/proc_macro_with_span.rs new file mode 100644 index 000000000000..8ea631f2bbd4 --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/auxiliary/proc_macro_with_span.rs @@ -0,0 +1,32 @@ +// compile-flags: --emit=link +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree}; + +#[proc_macro] +pub fn with_span(input: TokenStream) -> TokenStream { + let mut iter = input.into_iter(); + let span = iter.next().unwrap().span(); + let mut res = TokenStream::new(); + write_with_span(span, iter, &mut res); + res +} + +fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) { + for mut tt in input { + if let TokenTree::Group(g) = tt { + let mut stream = TokenStream::new(); + write_with_span(s, g.stream().into_iter(), &mut stream); + let mut group = Group::new(g.delimiter(), stream); + group.set_span(s); + out.extend([TokenTree::Group(group)]); + } else { + tt.set_span(s); + out.extend([tt]); + } + } +} diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml b/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml new file mode 100644 index 000000000000..b95e806aae24 --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml @@ -0,0 +1 @@ +allow-mixed-uninlined-format-args = false diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed new file mode 100644 index 000000000000..ca56c95c23f4 --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed @@ -0,0 +1,177 @@ +// aux-build:proc_macro_with_span.rs +// run-rustfix +#![feature(custom_inner_attributes)] +#![warn(clippy::uninlined_format_args)] +#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] +#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] + +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + +macro_rules! no_param_str { + () => { + "{}" + }; +} + +macro_rules! my_println { + ($($args:tt),*) => {{ + println!($($args),*) + }}; +} + +macro_rules! my_println_args { + ($($args:tt),*) => {{ + println!("foo: {}", format_args!($($args),*)) + }}; +} + +fn tester(fn_arg: i32) { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + let width = 4; + let prec = 5; + let val = 6; + + // make sure this file hasn't been corrupted with tabs converted to spaces + // let _ = ' '; // <- this is a single tab character + let _: &[u8; 3] = b" "; // <- + + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); // 3 spaces + println!("val='{local_i32}'"); // tab + println!("val='{local_i32}'"); // space+tab + println!("val='{local_i32}'"); // tab+space + println!( + "val='{local_i32}'" + ); + println!("{local_i32}"); + println!("{fn_arg}"); + println!("{local_i32:?}"); + println!("{local_i32:#?}"); + println!("{local_i32:4}"); + println!("{local_i32:04}"); + println!("{local_i32:<3}"); + println!("{local_i32:#010x}"); + println!("{local_f64:.1}"); + println!("Hello {} is {local_f64:.local_i32$}", "x"); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("{local_i32} {local_f64}"); + println!("{local_i32}, {}", local_opt.unwrap()); + println!("{val}"); + println!("{val}"); + println!("{} {1}", local_i32, 42); + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); + println!("val='{local_i32}'"); + println!("val='{fn_arg}'"); + println!("{local_i32}"); + println!("{local_i32:?}"); + println!("{local_i32:#?}"); + println!("{local_i32:04}"); + println!("{local_i32:<3}"); + println!("{local_i32:#010x}"); + println!("{local_f64:.1}"); + println!("{local_i32} {local_i32}"); + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); + println!("{local_i32} {local_f64}"); + println!("{local_f64} {local_i32}"); + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); + println!("{1} {0}", "str", local_i32); + println!("{local_i32}"); + println!("{local_i32:width$}"); + println!("{local_i32:width$}"); + println!("{local_i32:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{val:val$}"); + println!("{val:val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{val:val$.val$}"); + println!("{width:width$}"); + println!("{local_i32:width$}"); + println!("{width:width$}"); + println!("{local_i32:width$}"); + println!("{prec:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{prec:.prec$}"); + println!("{local_i32:.prec$}"); + println!("{width:width$.prec$}"); + println!("{width:width$.prec$}"); + println!("{local_f64:width$.prec$}"); + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); + println!( + "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", + ); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", + local_i32, + width, + prec, + 1 + 2 + ); + println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); + println!("{local_i32:width$.prec$}"); + println!("{width:width$.prec$}"); + println!("{}", format!("{local_i32}")); + my_println!("{}", local_i32); + my_println_args!("{}", local_i32); + + // these should NOT be modified by the lint + println!(concat!("nope ", "{}"), local_i32); + println!("val='{local_i32}'"); + println!("val='{local_i32 }'"); + println!("val='{local_i32 }'"); // with tab + println!("val='{local_i32\n}'"); + println!("{}", usize::MAX); + println!("{}", local_opt.unwrap()); + println!( + "val='{local_i32 + }'" + ); + println!(no_param_str!(), local_i32); + + println!( + "{val}", + ); + println!("{val}"); + + println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); + println!("{}", with_span!(span val)); + + if local_i32 > 0 { + panic!("p1 {local_i32}"); + } + if local_i32 > 0 { + panic!("p2 {local_i32}"); + } + if local_i32 > 0 { + panic!("p3 {local_i32}"); + } + if local_i32 > 0 { + panic!("p4 {local_i32}"); + } +} + +fn main() { + tester(42); +} + +fn _under_msrv() { + #![clippy::msrv = "1.57"] + let local_i32 = 1; + println!("don't expand='{}'", local_i32); +} + +fn _meets_msrv() { + #![clippy::msrv = "1.58"] + let local_i32 = 1; + println!("expand='{local_i32}'"); +} diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs new file mode 100644 index 000000000000..8e495ebd083a --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs @@ -0,0 +1,182 @@ +// aux-build:proc_macro_with_span.rs +// run-rustfix +#![feature(custom_inner_attributes)] +#![warn(clippy::uninlined_format_args)] +#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] +#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] + +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + +macro_rules! no_param_str { + () => { + "{}" + }; +} + +macro_rules! my_println { + ($($args:tt),*) => {{ + println!($($args),*) + }}; +} + +macro_rules! my_println_args { + ($($args:tt),*) => {{ + println!("foo: {}", format_args!($($args),*)) + }}; +} + +fn tester(fn_arg: i32) { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + let width = 4; + let prec = 5; + let val = 6; + + // make sure this file hasn't been corrupted with tabs converted to spaces + // let _ = ' '; // <- this is a single tab character + let _: &[u8; 3] = b" "; // <- + + println!("val='{}'", local_i32); + println!("val='{ }'", local_i32); // 3 spaces + println!("val='{ }'", local_i32); // tab + println!("val='{ }'", local_i32); // space+tab + println!("val='{ }'", local_i32); // tab+space + println!( + "val='{ + }'", + local_i32 + ); + println!("{}", local_i32); + println!("{}", fn_arg); + println!("{:?}", local_i32); + println!("{:#?}", local_i32); + println!("{:4}", local_i32); + println!("{:04}", local_i32); + println!("{:<3}", local_i32); + println!("{:#010x}", local_i32); + println!("{:.1}", local_f64); + println!("Hello {} is {:.*}", "x", local_i32, local_f64); + println!("Hello {} is {:.*}", local_i32, 5, local_f64); + println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + println!("{} {}", local_i32, local_f64); + println!("{}, {}", local_i32, local_opt.unwrap()); + println!("{}", val); + println!("{}", v = val); + println!("{} {1}", local_i32, 42); + println!("val='{\t }'", local_i32); + println!("val='{\n }'", local_i32); + println!("val='{local_i32}'", local_i32 = local_i32); + println!("val='{local_i32}'", local_i32 = fn_arg); + println!("{0}", local_i32); + println!("{0:?}", local_i32); + println!("{0:#?}", local_i32); + println!("{0:04}", local_i32); + println!("{0:<3}", local_i32); + println!("{0:#010x}", local_i32); + println!("{0:.1}", local_f64); + println!("{0} {0}", local_i32); + println!("{1} {} {0} {}", local_i32, local_f64); + println!("{0} {1}", local_i32, local_f64); + println!("{1} {0}", local_i32, local_f64); + println!("{1} {0} {1} {0}", local_i32, local_f64); + println!("{1} {0}", "str", local_i32); + println!("{v}", v = local_i32); + println!("{local_i32:0$}", width); + println!("{local_i32:w$}", w = width); + println!("{local_i32:.0$}", prec); + println!("{local_i32:.p$}", p = prec); + println!("{:0$}", v = val); + println!("{0:0$}", v = val); + println!("{:0$.0$}", v = val); + println!("{0:0$.0$}", v = val); + println!("{0:0$.v$}", v = val); + println!("{0:v$.0$}", v = val); + println!("{v:0$.0$}", v = val); + println!("{v:v$.0$}", v = val); + println!("{v:0$.v$}", v = val); + println!("{v:v$.v$}", v = val); + println!("{:0$}", width); + println!("{:1$}", local_i32, width); + println!("{:w$}", w = width); + println!("{:w$}", local_i32, w = width); + println!("{:.0$}", prec); + println!("{:.1$}", local_i32, prec); + println!("{:.p$}", p = prec); + println!("{:.p$}", local_i32, p = prec); + println!("{:0$.1$}", width, prec); + println!("{:0$.w$}", width, w = prec); + println!("{:1$.2$}", local_f64, width, prec); + println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", + local_i32, width, prec, + ); + println!( + "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", + local_i32, + width, + prec, + 1 + 2 + ); + println!("Width = {}, value with width = {:0$}", local_i32, local_f64); + println!("{:w$.p$}", local_i32, w = width, p = prec); + println!("{:w$.p$}", w = width, p = prec); + println!("{}", format!("{}", local_i32)); + my_println!("{}", local_i32); + my_println_args!("{}", local_i32); + + // these should NOT be modified by the lint + println!(concat!("nope ", "{}"), local_i32); + println!("val='{local_i32}'"); + println!("val='{local_i32 }'"); + println!("val='{local_i32 }'"); // with tab + println!("val='{local_i32\n}'"); + println!("{}", usize::MAX); + println!("{}", local_opt.unwrap()); + println!( + "val='{local_i32 + }'" + ); + println!(no_param_str!(), local_i32); + + println!( + "{}", + // comment with a comma , in it + val, + ); + println!("{}", /* comment with a comma , in it */ val); + + println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); + println!("{}", with_span!(span val)); + + if local_i32 > 0 { + panic!("p1 {}", local_i32); + } + if local_i32 > 0 { + panic!("p2 {0}", local_i32); + } + if local_i32 > 0 { + panic!("p3 {local_i32}", local_i32 = local_i32); + } + if local_i32 > 0 { + panic!("p4 {local_i32}"); + } +} + +fn main() { + tester(42); +} + +fn _under_msrv() { + #![clippy::msrv = "1.57"] + let local_i32 = 1; + println!("don't expand='{}'", local_i32); +} + +fn _meets_msrv() { + #![clippy::msrv = "1.58"] + let local_i32 = 1; + println!("expand='{}'", local_i32); +} diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr new file mode 100644 index 000000000000..1182d57ce9b7 --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr @@ -0,0 +1,908 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:41:5 + | +LL | println!("val='{}'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:42:5 + | +LL | println!("val='{ }'", local_i32); // 3 spaces + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // 3 spaces +LL + println!("val='{local_i32}'"); // 3 spaces + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:43:5 + | +LL | println!("val='{ }'", local_i32); // tab + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // tab +LL + println!("val='{local_i32}'"); // tab + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:44:5 + | +LL | println!("val='{ }'", local_i32); // space+tab + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // space+tab +LL + println!("val='{local_i32}'"); // space+tab + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:45:5 + | +LL | println!("val='{ }'", local_i32); // tab+space + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{ }'", local_i32); // tab+space +LL + println!("val='{local_i32}'"); // tab+space + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:46:5 + | +LL | / println!( +LL | | "val='{ +LL | | }'", +LL | | local_i32 +LL | | ); + | |_____^ + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:51:5 + | +LL | println!("{}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:52:5 + | +LL | println!("{}", fn_arg); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", fn_arg); +LL + println!("{fn_arg}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:53:5 + | +LL | println!("{:?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:?}", local_i32); +LL + println!("{local_i32:?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:54:5 + | +LL | println!("{:#?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:#?}", local_i32); +LL + println!("{local_i32:#?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:55:5 + | +LL | println!("{:4}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:4}", local_i32); +LL + println!("{local_i32:4}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:56:5 + | +LL | println!("{:04}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:04}", local_i32); +LL + println!("{local_i32:04}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:57:5 + | +LL | println!("{:<3}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:<3}", local_i32); +LL + println!("{local_i32:<3}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:58:5 + | +LL | println!("{:#010x}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:#010x}", local_i32); +LL + println!("{local_i32:#010x}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:59:5 + | +LL | println!("{:.1}", local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.1}", local_f64); +LL + println!("{local_f64:.1}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:60:5 + | +LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); +LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:61:5 + | +LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:62:5 + | +LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:63:5 + | +LL | println!("{} {}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{} {}", local_i32, local_f64); +LL + println!("{local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:64:5 + | +LL | println!("{}, {}", local_i32, local_opt.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}, {}", local_i32, local_opt.unwrap()); +LL + println!("{local_i32}, {}", local_opt.unwrap()); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:65:5 + | +LL | println!("{}", val); + | ^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:66:5 + | +LL | println!("{}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", v = val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:68:5 + | +LL | println!("val='{/t }'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{/t }'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:69:5 + | +LL | println!("val='{/n }'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{/n }'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:70:5 + | +LL | println!("val='{local_i32}'", local_i32 = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{local_i32}'", local_i32 = local_i32); +LL + println!("val='{local_i32}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:71:5 + | +LL | println!("val='{local_i32}'", local_i32 = fn_arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("val='{local_i32}'", local_i32 = fn_arg); +LL + println!("val='{fn_arg}'"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:72:5 + | +LL | println!("{0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0}", local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:73:5 + | +LL | println!("{0:?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:?}", local_i32); +LL + println!("{local_i32:?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:74:5 + | +LL | println!("{0:#?}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:#?}", local_i32); +LL + println!("{local_i32:#?}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:75:5 + | +LL | println!("{0:04}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:04}", local_i32); +LL + println!("{local_i32:04}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:76:5 + | +LL | println!("{0:<3}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:<3}", local_i32); +LL + println!("{local_i32:<3}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:77:5 + | +LL | println!("{0:#010x}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:#010x}", local_i32); +LL + println!("{local_i32:#010x}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:78:5 + | +LL | println!("{0:.1}", local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:.1}", local_f64); +LL + println!("{local_f64:.1}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:79:5 + | +LL | println!("{0} {0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0} {0}", local_i32); +LL + println!("{local_i32} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:80:5 + | +LL | println!("{1} {} {0} {}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {} {0} {}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:81:5 + | +LL | println!("{0} {1}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0} {1}", local_i32, local_f64); +LL + println!("{local_i32} {local_f64}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:82:5 + | +LL | println!("{1} {0}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {0}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:83:5 + | +LL | println!("{1} {0} {1} {0}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{1} {0} {1} {0}", local_i32, local_f64); +LL + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:85:5 + | +LL | println!("{v}", v = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v}", v = local_i32); +LL + println!("{local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:86:5 + | +LL | println!("{local_i32:0$}", width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:0$}", width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:87:5 + | +LL | println!("{local_i32:w$}", w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:w$}", w = width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:88:5 + | +LL | println!("{local_i32:.0$}", prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:.0$}", prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:89:5 + | +LL | println!("{local_i32:.p$}", p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{local_i32:.p$}", p = prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:90:5 + | +LL | println!("{:0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$}", v = val); +LL + println!("{val:val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:91:5 + | +LL | println!("{0:0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$}", v = val); +LL + println!("{val:val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:92:5 + | +LL | println!("{:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:93:5 + | +LL | println!("{0:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:94:5 + | +LL | println!("{0:0$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:0$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:95:5 + | +LL | println!("{0:v$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{0:v$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:96:5 + | +LL | println!("{v:0$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:0$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:97:5 + | +LL | println!("{v:v$.0$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:v$.0$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:98:5 + | +LL | println!("{v:0$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:0$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:99:5 + | +LL | println!("{v:v$.v$}", v = val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{v:v$.v$}", v = val); +LL + println!("{val:val$.val$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:100:5 + | +LL | println!("{:0$}", width); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$}", width); +LL + println!("{width:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:101:5 + | +LL | println!("{:1$}", local_i32, width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$}", local_i32, width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:102:5 + | +LL | println!("{:w$}", w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$}", w = width); +LL + println!("{width:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:103:5 + | +LL | println!("{:w$}", local_i32, w = width); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$}", local_i32, w = width); +LL + println!("{local_i32:width$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:104:5 + | +LL | println!("{:.0$}", prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.0$}", prec); +LL + println!("{prec:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:105:5 + | +LL | println!("{:.1$}", local_i32, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.1$}", local_i32, prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:106:5 + | +LL | println!("{:.p$}", p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.p$}", p = prec); +LL + println!("{prec:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:107:5 + | +LL | println!("{:.p$}", local_i32, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:.p$}", local_i32, p = prec); +LL + println!("{local_i32:.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:108:5 + | +LL | println!("{:0$.1$}", width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.1$}", width, prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:109:5 + | +LL | println!("{:0$.w$}", width, w = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:0$.w$}", width, w = prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:110:5 + | +LL | println!("{:1$.2$}", local_f64, width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$.2$}", local_f64, width, prec); +LL + println!("{local_f64:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:111:5 + | +LL | println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); +LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:112:5 + | +LL | / println!( +LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", +LL | | local_i32, width, prec, +LL | | ); + | |_____^ + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:123:5 + | +LL | println!("Width = {}, value with width = {:0$}", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Width = {}, value with width = {:0$}", local_i32, local_f64); +LL + println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:124:5 + | +LL | println!("{:w$.p$}", local_i32, w = width, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$.p$}", local_i32, w = width, p = prec); +LL + println!("{local_i32:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:125:5 + | +LL | println!("{:w$.p$}", w = width, p = prec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{:w$.p$}", w = width, p = prec); +LL + println!("{width:width$.prec$}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:126:20 + | +LL | println!("{}", format!("{}", local_i32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", format!("{}", local_i32)); +LL + println!("{}", format!("{local_i32}")); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:144:5 + | +LL | / println!( +LL | | "{}", +LL | | // comment with a comma , in it +LL | | val, +LL | | ); + | |_____^ + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:149:5 + | +LL | println!("{}", /* comment with a comma , in it */ val); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}", /* comment with a comma , in it */ val); +LL + println!("{val}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:155:9 + | +LL | panic!("p1 {}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p1 {}", local_i32); +LL + panic!("p1 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:158:9 + | +LL | panic!("p2 {0}", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p2 {0}", local_i32); +LL + panic!("p2 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:161:9 + | +LL | panic!("p3 {local_i32}", local_i32 = local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - panic!("p3 {local_i32}", local_i32 = local_i32); +LL + panic!("p3 {local_i32}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:181:5 + | +LL | println!("expand='{}'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("expand='{}'", local_i32); +LL + println!("expand='{local_i32}'"); + | + +error: aborting due to 76 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index f91d285c2e0e..01a5e962c949 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,6 +1,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of allow-dbg-in-tests allow-expect-in-tests + allow-mixed-uninlined-format-args allow-print-in-tests allow-unwrap-in-tests allowed-scripts diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index 46ec7771114f..9d08e80cf9a5 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -54,11 +54,11 @@ fn tester(fn_arg: i32) { println!("{local_i32:<3}"); println!("{local_i32:#010x}"); println!("{local_f64:.1}"); - println!("Hello {} is {local_f64:.local_i32$}", "x"); - println!("Hello {local_i32} is {local_f64:.*}", 5); - println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("Hello {} is {:.*}", "x", local_i32, local_f64); + println!("Hello {} is {:.*}", local_i32, 5, local_f64); + println!("Hello {} is {2:.*}", local_i32, 5, local_f64); println!("{local_i32} {local_f64}"); - println!("{local_i32}, {}", local_opt.unwrap()); + println!("{}, {}", local_i32, local_opt.unwrap()); println!("{val}"); println!("{val}"); println!("{} {1}", local_i32, 42); diff --git a/tests/ui/uninlined_format_args_panic.edition2018.stderr b/tests/ui/uninlined_format_args_panic.edition2018.stderr index 2c8061259229..1afdb4d0fbab 100644 --- a/tests/ui/uninlined_format_args_panic.edition2018.stderr +++ b/tests/ui/uninlined_format_args_panic.edition2018.stderr @@ -4,6 +4,7 @@ error: variables can be used directly in the `format!` string LL | println!("val='{}'", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this lint can also fix mixed format arg inlining if `allow-mixed-uninlined-format-args = false` is set in the `clippy.toml` file = note: `-D clippy::uninlined-format-args` implied by `-D warnings` help: change this to | diff --git a/tests/ui/uninlined_format_args_panic.edition2021.stderr b/tests/ui/uninlined_format_args_panic.edition2021.stderr index 0f09c45f4132..a38ea4168bc9 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.stderr +++ b/tests/ui/uninlined_format_args_panic.edition2021.stderr @@ -4,6 +4,7 @@ error: variables can be used directly in the `format!` string LL | println!("val='{}'", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this lint can also fix mixed format arg inlining if `allow-mixed-uninlined-format-args = false` is set in the `clippy.toml` file = note: `-D clippy::uninlined-format-args` implied by `-D warnings` help: change this to | From 13dfbb2fa526afc4d425459868d7951e248ba65c Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 24 Oct 2022 16:33:53 -0700 Subject: [PATCH 277/524] Migrate from highfive to triagebot --- triagebot.toml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 80c30393832c..c615d18f8687 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -4,9 +4,23 @@ allow-unauthenticated = [ "good-first-issue" ] -[assign] - # Allows shortcuts like `@rustbot ready` # # See https://github.com/rust-lang/triagebot/wiki/Shortcuts [shortcut] + +[assign] +contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" + +[assign.owners] +"/.github" = ["@flip1995"] +"*" = [ + "@flip1995", + "@Manishearth", + "@llogiq", + "@giraffate", + "@xFrednet", + "@Alexendoo", + "@dswij", + "@Jarcho", +] From ab576afc18e0b15167388c1a294587962a94d264 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sun, 27 Nov 2022 10:34:13 -0500 Subject: [PATCH 278/524] addressed review feedback --- clippy_lints/src/format_args.rs | 7 - .../auxiliary/proc_macro_with_span.rs | 32 - .../uninlined_format_args.fixed | 167 +--- .../uninlined_format_args.rs | 170 +--- .../uninlined_format_args.stderr | 860 +----------------- tests/ui/uninlined_format_args.stderr | 50 +- ...lined_format_args_panic.edition2018.stderr | 1 - ...lined_format_args_panic.edition2021.stderr | 1 - 8 files changed, 18 insertions(+), 1270 deletions(-) delete mode 100644 tests/ui-toml/allow_mixed_uninlined_format_args/auxiliary/proc_macro_with_span.rs diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 40a37d39ecf8..f0995a81329d 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -321,13 +321,6 @@ fn check_uninlined_args( Applicability::MachineApplicable, if multiline_fix { CompletelyHidden } else { ShowCode }, ); - if ignore_mixed { - // Improve lint config discoverability - diag.note_once( - "this lint can also fix mixed format arg inlining if \ - `allow-mixed-uninlined-format-args = false` is set in the `clippy.toml` file", - ); - } }, ); } diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/auxiliary/proc_macro_with_span.rs b/tests/ui-toml/allow_mixed_uninlined_format_args/auxiliary/proc_macro_with_span.rs deleted file mode 100644 index 8ea631f2bbd4..000000000000 --- a/tests/ui-toml/allow_mixed_uninlined_format_args/auxiliary/proc_macro_with_span.rs +++ /dev/null @@ -1,32 +0,0 @@ -// compile-flags: --emit=link -// no-prefer-dynamic - -#![crate_type = "proc-macro"] - -extern crate proc_macro; - -use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree}; - -#[proc_macro] -pub fn with_span(input: TokenStream) -> TokenStream { - let mut iter = input.into_iter(); - let span = iter.next().unwrap().span(); - let mut res = TokenStream::new(); - write_with_span(span, iter, &mut res); - res -} - -fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) { - for mut tt in input { - if let TokenTree::Group(g) = tt { - let mut stream = TokenStream::new(); - write_with_span(s, g.stream().into_iter(), &mut stream); - let mut group = Group::new(g.delimiter(), stream); - group.set_span(s); - out.extend([TokenTree::Group(group)]); - } else { - tt.set_span(s); - out.extend([tt]); - } - } -} diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed index ca56c95c23f4..aa8b45b5fe7d 100644 --- a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed @@ -1,177 +1,14 @@ -// aux-build:proc_macro_with_span.rs // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::uninlined_format_args)] -#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] -#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] -extern crate proc_macro_with_span; -use proc_macro_with_span::with_span; - -macro_rules! no_param_str { - () => { - "{}" - }; -} - -macro_rules! my_println { - ($($args:tt),*) => {{ - println!($($args),*) - }}; -} - -macro_rules! my_println_args { - ($($args:tt),*) => {{ - println!("foo: {}", format_args!($($args),*)) - }}; -} - -fn tester(fn_arg: i32) { +fn main() { let local_i32 = 1; let local_f64 = 2.0; let local_opt: Option = Some(3); - let width = 4; - let prec = 5; - let val = 6; - - // make sure this file hasn't been corrupted with tabs converted to spaces - // let _ = ' '; // <- this is a single tab character - let _: &[u8; 3] = b" "; // <- println!("val='{local_i32}'"); - println!("val='{local_i32}'"); // 3 spaces - println!("val='{local_i32}'"); // tab - println!("val='{local_i32}'"); // space+tab - println!("val='{local_i32}'"); // tab+space - println!( - "val='{local_i32}'" - ); - println!("{local_i32}"); - println!("{fn_arg}"); - println!("{local_i32:?}"); - println!("{local_i32:#?}"); - println!("{local_i32:4}"); - println!("{local_i32:04}"); - println!("{local_i32:<3}"); - println!("{local_i32:#010x}"); - println!("{local_f64:.1}"); - println!("Hello {} is {local_f64:.local_i32$}", "x"); + println!("Hello x is {local_f64:.local_i32$}"); println!("Hello {local_i32} is {local_f64:.*}", 5); println!("Hello {local_i32} is {local_f64:.*}", 5); - println!("{local_i32} {local_f64}"); println!("{local_i32}, {}", local_opt.unwrap()); - println!("{val}"); - println!("{val}"); - println!("{} {1}", local_i32, 42); - println!("val='{local_i32}'"); - println!("val='{local_i32}'"); - println!("val='{local_i32}'"); - println!("val='{fn_arg}'"); - println!("{local_i32}"); - println!("{local_i32:?}"); - println!("{local_i32:#?}"); - println!("{local_i32:04}"); - println!("{local_i32:<3}"); - println!("{local_i32:#010x}"); - println!("{local_f64:.1}"); - println!("{local_i32} {local_i32}"); - println!("{local_f64} {local_i32} {local_i32} {local_f64}"); - println!("{local_i32} {local_f64}"); - println!("{local_f64} {local_i32}"); - println!("{local_f64} {local_i32} {local_f64} {local_i32}"); - println!("{1} {0}", "str", local_i32); - println!("{local_i32}"); - println!("{local_i32:width$}"); - println!("{local_i32:width$}"); - println!("{local_i32:.prec$}"); - println!("{local_i32:.prec$}"); - println!("{val:val$}"); - println!("{val:val$}"); - println!("{val:val$.val$}"); - println!("{val:val$.val$}"); - println!("{val:val$.val$}"); - println!("{val:val$.val$}"); - println!("{val:val$.val$}"); - println!("{val:val$.val$}"); - println!("{val:val$.val$}"); - println!("{val:val$.val$}"); - println!("{width:width$}"); - println!("{local_i32:width$}"); - println!("{width:width$}"); - println!("{local_i32:width$}"); - println!("{prec:.prec$}"); - println!("{local_i32:.prec$}"); - println!("{prec:.prec$}"); - println!("{local_i32:.prec$}"); - println!("{width:width$.prec$}"); - println!("{width:width$.prec$}"); - println!("{local_f64:width$.prec$}"); - println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); - println!( - "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", - ); - println!( - "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", - local_i32, - width, - prec, - 1 + 2 - ); - println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); - println!("{local_i32:width$.prec$}"); - println!("{width:width$.prec$}"); - println!("{}", format!("{local_i32}")); - my_println!("{}", local_i32); - my_println_args!("{}", local_i32); - - // these should NOT be modified by the lint - println!(concat!("nope ", "{}"), local_i32); - println!("val='{local_i32}'"); - println!("val='{local_i32 }'"); - println!("val='{local_i32 }'"); // with tab - println!("val='{local_i32\n}'"); - println!("{}", usize::MAX); - println!("{}", local_opt.unwrap()); - println!( - "val='{local_i32 - }'" - ); - println!(no_param_str!(), local_i32); - - println!( - "{val}", - ); - println!("{val}"); - - println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); - println!("{}", with_span!(span val)); - - if local_i32 > 0 { - panic!("p1 {local_i32}"); - } - if local_i32 > 0 { - panic!("p2 {local_i32}"); - } - if local_i32 > 0 { - panic!("p3 {local_i32}"); - } - if local_i32 > 0 { - panic!("p4 {local_i32}"); - } -} - -fn main() { - tester(42); -} - -fn _under_msrv() { - #![clippy::msrv = "1.57"] - let local_i32 = 1; - println!("don't expand='{}'", local_i32); -} - -fn _meets_msrv() { - #![clippy::msrv = "1.58"] - let local_i32 = 1; - println!("expand='{local_i32}'"); } diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs index 8e495ebd083a..ad2e4863ee8e 100644 --- a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs @@ -1,182 +1,14 @@ -// aux-build:proc_macro_with_span.rs // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::uninlined_format_args)] -#![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] -#![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] -extern crate proc_macro_with_span; -use proc_macro_with_span::with_span; - -macro_rules! no_param_str { - () => { - "{}" - }; -} - -macro_rules! my_println { - ($($args:tt),*) => {{ - println!($($args),*) - }}; -} - -macro_rules! my_println_args { - ($($args:tt),*) => {{ - println!("foo: {}", format_args!($($args),*)) - }}; -} - -fn tester(fn_arg: i32) { +fn main() { let local_i32 = 1; let local_f64 = 2.0; let local_opt: Option = Some(3); - let width = 4; - let prec = 5; - let val = 6; - - // make sure this file hasn't been corrupted with tabs converted to spaces - // let _ = ' '; // <- this is a single tab character - let _: &[u8; 3] = b" "; // <- println!("val='{}'", local_i32); - println!("val='{ }'", local_i32); // 3 spaces - println!("val='{ }'", local_i32); // tab - println!("val='{ }'", local_i32); // space+tab - println!("val='{ }'", local_i32); // tab+space - println!( - "val='{ - }'", - local_i32 - ); - println!("{}", local_i32); - println!("{}", fn_arg); - println!("{:?}", local_i32); - println!("{:#?}", local_i32); - println!("{:4}", local_i32); - println!("{:04}", local_i32); - println!("{:<3}", local_i32); - println!("{:#010x}", local_i32); - println!("{:.1}", local_f64); println!("Hello {} is {:.*}", "x", local_i32, local_f64); println!("Hello {} is {:.*}", local_i32, 5, local_f64); println!("Hello {} is {2:.*}", local_i32, 5, local_f64); - println!("{} {}", local_i32, local_f64); println!("{}, {}", local_i32, local_opt.unwrap()); - println!("{}", val); - println!("{}", v = val); - println!("{} {1}", local_i32, 42); - println!("val='{\t }'", local_i32); - println!("val='{\n }'", local_i32); - println!("val='{local_i32}'", local_i32 = local_i32); - println!("val='{local_i32}'", local_i32 = fn_arg); - println!("{0}", local_i32); - println!("{0:?}", local_i32); - println!("{0:#?}", local_i32); - println!("{0:04}", local_i32); - println!("{0:<3}", local_i32); - println!("{0:#010x}", local_i32); - println!("{0:.1}", local_f64); - println!("{0} {0}", local_i32); - println!("{1} {} {0} {}", local_i32, local_f64); - println!("{0} {1}", local_i32, local_f64); - println!("{1} {0}", local_i32, local_f64); - println!("{1} {0} {1} {0}", local_i32, local_f64); - println!("{1} {0}", "str", local_i32); - println!("{v}", v = local_i32); - println!("{local_i32:0$}", width); - println!("{local_i32:w$}", w = width); - println!("{local_i32:.0$}", prec); - println!("{local_i32:.p$}", p = prec); - println!("{:0$}", v = val); - println!("{0:0$}", v = val); - println!("{:0$.0$}", v = val); - println!("{0:0$.0$}", v = val); - println!("{0:0$.v$}", v = val); - println!("{0:v$.0$}", v = val); - println!("{v:0$.0$}", v = val); - println!("{v:v$.0$}", v = val); - println!("{v:0$.v$}", v = val); - println!("{v:v$.v$}", v = val); - println!("{:0$}", width); - println!("{:1$}", local_i32, width); - println!("{:w$}", w = width); - println!("{:w$}", local_i32, w = width); - println!("{:.0$}", prec); - println!("{:.1$}", local_i32, prec); - println!("{:.p$}", p = prec); - println!("{:.p$}", local_i32, p = prec); - println!("{:0$.1$}", width, prec); - println!("{:0$.w$}", width, w = prec); - println!("{:1$.2$}", local_f64, width, prec); - println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); - println!( - "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", - local_i32, width, prec, - ); - println!( - "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", - local_i32, - width, - prec, - 1 + 2 - ); - println!("Width = {}, value with width = {:0$}", local_i32, local_f64); - println!("{:w$.p$}", local_i32, w = width, p = prec); - println!("{:w$.p$}", w = width, p = prec); - println!("{}", format!("{}", local_i32)); - my_println!("{}", local_i32); - my_println_args!("{}", local_i32); - - // these should NOT be modified by the lint - println!(concat!("nope ", "{}"), local_i32); - println!("val='{local_i32}'"); - println!("val='{local_i32 }'"); - println!("val='{local_i32 }'"); // with tab - println!("val='{local_i32\n}'"); - println!("{}", usize::MAX); - println!("{}", local_opt.unwrap()); - println!( - "val='{local_i32 - }'" - ); - println!(no_param_str!(), local_i32); - - println!( - "{}", - // comment with a comma , in it - val, - ); - println!("{}", /* comment with a comma , in it */ val); - - println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64); - println!("{}", with_span!(span val)); - - if local_i32 > 0 { - panic!("p1 {}", local_i32); - } - if local_i32 > 0 { - panic!("p2 {0}", local_i32); - } - if local_i32 > 0 { - panic!("p3 {local_i32}", local_i32 = local_i32); - } - if local_i32 > 0 { - panic!("p4 {local_i32}"); - } -} - -fn main() { - tester(42); -} - -fn _under_msrv() { - #![clippy::msrv = "1.57"] - let local_i32 = 1; - println!("don't expand='{}'", local_i32); -} - -fn _meets_msrv() { - #![clippy::msrv = "1.58"] - let local_i32 = 1; - println!("expand='{}'", local_i32); } diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr index 1182d57ce9b7..ee9417621961 100644 --- a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr @@ -1,5 +1,5 @@ error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:41:5 + --> $DIR/uninlined_format_args.rs:9:5 | LL | println!("val='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -11,174 +11,21 @@ LL - println!("val='{}'", local_i32); LL + println!("val='{local_i32}'"); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:42:5 - | -LL | println!("val='{ }'", local_i32); // 3 spaces - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("val='{ }'", local_i32); // 3 spaces -LL + println!("val='{local_i32}'"); // 3 spaces - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:43:5 - | -LL | println!("val='{ }'", local_i32); // tab - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("val='{ }'", local_i32); // tab -LL + println!("val='{local_i32}'"); // tab - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:44:5 - | -LL | println!("val='{ }'", local_i32); // space+tab - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("val='{ }'", local_i32); // space+tab -LL + println!("val='{local_i32}'"); // space+tab - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:45:5 - | -LL | println!("val='{ }'", local_i32); // tab+space - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("val='{ }'", local_i32); // tab+space -LL + println!("val='{local_i32}'"); // tab+space - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:46:5 - | -LL | / println!( -LL | | "val='{ -LL | | }'", -LL | | local_i32 -LL | | ); - | |_____^ - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:51:5 - | -LL | println!("{}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{}", local_i32); -LL + println!("{local_i32}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:52:5 - | -LL | println!("{}", fn_arg); - | ^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{}", fn_arg); -LL + println!("{fn_arg}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:53:5 - | -LL | println!("{:?}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: literal with an empty format string + --> $DIR/uninlined_format_args.rs:10:35 | -help: change this to - | -LL - println!("{:?}", local_i32); -LL + println!("{local_i32:?}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:54:5 - | -LL | println!("{:#?}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:#?}", local_i32); -LL + println!("{local_i32:#?}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:55:5 - | -LL | println!("{:4}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:4}", local_i32); -LL + println!("{local_i32:4}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:56:5 - | -LL | println!("{:04}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:04}", local_i32); -LL + println!("{local_i32:04}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:57:5 - | -LL | println!("{:<3}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:<3}", local_i32); -LL + println!("{local_i32:<3}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:58:5 - | -LL | println!("{:#010x}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:#010x}", local_i32); -LL + println!("{local_i32:#010x}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:59:5 +LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); + | ^^^ | -LL | println!("{:.1}", local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::print-literal` implied by `-D warnings` +help: try this | -help: change this to - | -LL - println!("{:.1}", local_f64); -LL + println!("{local_f64:.1}"); +LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); +LL + println!("Hello x is {:.*}", local_i32, local_f64); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:60:5 + --> $DIR/uninlined_format_args.rs:10:5 | LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -190,7 +37,7 @@ LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:61:5 + --> $DIR/uninlined_format_args.rs:11:5 | LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -202,7 +49,7 @@ LL + println!("Hello {local_i32} is {local_f64:.*}", 5); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:62:5 + --> $DIR/uninlined_format_args.rs:12:5 | LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -214,19 +61,7 @@ LL + println!("Hello {local_i32} is {local_f64:.*}", 5); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:63:5 - | -LL | println!("{} {}", local_i32, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{} {}", local_i32, local_f64); -LL + println!("{local_i32} {local_f64}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:64:5 + --> $DIR/uninlined_format_args.rs:13:5 | LL | println!("{}, {}", local_i32, local_opt.unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -237,672 +72,5 @@ LL - println!("{}, {}", local_i32, local_opt.unwrap()); LL + println!("{local_i32}, {}", local_opt.unwrap()); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:65:5 - | -LL | println!("{}", val); - | ^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{}", val); -LL + println!("{val}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:66:5 - | -LL | println!("{}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{}", v = val); -LL + println!("{val}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:68:5 - | -LL | println!("val='{/t }'", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("val='{/t }'", local_i32); -LL + println!("val='{local_i32}'"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:69:5 - | -LL | println!("val='{/n }'", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("val='{/n }'", local_i32); -LL + println!("val='{local_i32}'"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:70:5 - | -LL | println!("val='{local_i32}'", local_i32 = local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("val='{local_i32}'", local_i32 = local_i32); -LL + println!("val='{local_i32}'"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:71:5 - | -LL | println!("val='{local_i32}'", local_i32 = fn_arg); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("val='{local_i32}'", local_i32 = fn_arg); -LL + println!("val='{fn_arg}'"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:72:5 - | -LL | println!("{0}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0}", local_i32); -LL + println!("{local_i32}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:73:5 - | -LL | println!("{0:?}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0:?}", local_i32); -LL + println!("{local_i32:?}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:74:5 - | -LL | println!("{0:#?}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0:#?}", local_i32); -LL + println!("{local_i32:#?}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:75:5 - | -LL | println!("{0:04}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0:04}", local_i32); -LL + println!("{local_i32:04}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:76:5 - | -LL | println!("{0:<3}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0:<3}", local_i32); -LL + println!("{local_i32:<3}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:77:5 - | -LL | println!("{0:#010x}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0:#010x}", local_i32); -LL + println!("{local_i32:#010x}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:78:5 - | -LL | println!("{0:.1}", local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0:.1}", local_f64); -LL + println!("{local_f64:.1}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:79:5 - | -LL | println!("{0} {0}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0} {0}", local_i32); -LL + println!("{local_i32} {local_i32}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:80:5 - | -LL | println!("{1} {} {0} {}", local_i32, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{1} {} {0} {}", local_i32, local_f64); -LL + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:81:5 - | -LL | println!("{0} {1}", local_i32, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0} {1}", local_i32, local_f64); -LL + println!("{local_i32} {local_f64}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:82:5 - | -LL | println!("{1} {0}", local_i32, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{1} {0}", local_i32, local_f64); -LL + println!("{local_f64} {local_i32}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:83:5 - | -LL | println!("{1} {0} {1} {0}", local_i32, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{1} {0} {1} {0}", local_i32, local_f64); -LL + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:85:5 - | -LL | println!("{v}", v = local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{v}", v = local_i32); -LL + println!("{local_i32}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:86:5 - | -LL | println!("{local_i32:0$}", width); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{local_i32:0$}", width); -LL + println!("{local_i32:width$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:87:5 - | -LL | println!("{local_i32:w$}", w = width); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{local_i32:w$}", w = width); -LL + println!("{local_i32:width$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:88:5 - | -LL | println!("{local_i32:.0$}", prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{local_i32:.0$}", prec); -LL + println!("{local_i32:.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:89:5 - | -LL | println!("{local_i32:.p$}", p = prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{local_i32:.p$}", p = prec); -LL + println!("{local_i32:.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:90:5 - | -LL | println!("{:0$}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:0$}", v = val); -LL + println!("{val:val$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:91:5 - | -LL | println!("{0:0$}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0:0$}", v = val); -LL + println!("{val:val$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:92:5 - | -LL | println!("{:0$.0$}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:0$.0$}", v = val); -LL + println!("{val:val$.val$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:93:5 - | -LL | println!("{0:0$.0$}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0:0$.0$}", v = val); -LL + println!("{val:val$.val$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:94:5 - | -LL | println!("{0:0$.v$}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0:0$.v$}", v = val); -LL + println!("{val:val$.val$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:95:5 - | -LL | println!("{0:v$.0$}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{0:v$.0$}", v = val); -LL + println!("{val:val$.val$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:96:5 - | -LL | println!("{v:0$.0$}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{v:0$.0$}", v = val); -LL + println!("{val:val$.val$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:97:5 - | -LL | println!("{v:v$.0$}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{v:v$.0$}", v = val); -LL + println!("{val:val$.val$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:98:5 - | -LL | println!("{v:0$.v$}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{v:0$.v$}", v = val); -LL + println!("{val:val$.val$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:99:5 - | -LL | println!("{v:v$.v$}", v = val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{v:v$.v$}", v = val); -LL + println!("{val:val$.val$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:100:5 - | -LL | println!("{:0$}", width); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:0$}", width); -LL + println!("{width:width$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:101:5 - | -LL | println!("{:1$}", local_i32, width); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:1$}", local_i32, width); -LL + println!("{local_i32:width$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:102:5 - | -LL | println!("{:w$}", w = width); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:w$}", w = width); -LL + println!("{width:width$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:103:5 - | -LL | println!("{:w$}", local_i32, w = width); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:w$}", local_i32, w = width); -LL + println!("{local_i32:width$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:104:5 - | -LL | println!("{:.0$}", prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:.0$}", prec); -LL + println!("{prec:.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:105:5 - | -LL | println!("{:.1$}", local_i32, prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:.1$}", local_i32, prec); -LL + println!("{local_i32:.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:106:5 - | -LL | println!("{:.p$}", p = prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:.p$}", p = prec); -LL + println!("{prec:.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:107:5 - | -LL | println!("{:.p$}", local_i32, p = prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:.p$}", local_i32, p = prec); -LL + println!("{local_i32:.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:108:5 - | -LL | println!("{:0$.1$}", width, prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:0$.1$}", width, prec); -LL + println!("{width:width$.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:109:5 - | -LL | println!("{:0$.w$}", width, w = prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:0$.w$}", width, w = prec); -LL + println!("{width:width$.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:110:5 - | -LL | println!("{:1$.2$}", local_f64, width, prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:1$.2$}", local_f64, width, prec); -LL + println!("{local_f64:width$.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:111:5 - | -LL | println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); -LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:112:5 - | -LL | / println!( -LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", -LL | | local_i32, width, prec, -LL | | ); - | |_____^ - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:123:5 - | -LL | println!("Width = {}, value with width = {:0$}", local_i32, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Width = {}, value with width = {:0$}", local_i32, local_f64); -LL + println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:124:5 - | -LL | println!("{:w$.p$}", local_i32, w = width, p = prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:w$.p$}", local_i32, w = width, p = prec); -LL + println!("{local_i32:width$.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:125:5 - | -LL | println!("{:w$.p$}", w = width, p = prec); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{:w$.p$}", w = width, p = prec); -LL + println!("{width:width$.prec$}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:126:20 - | -LL | println!("{}", format!("{}", local_i32)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{}", format!("{}", local_i32)); -LL + println!("{}", format!("{local_i32}")); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:144:5 - | -LL | / println!( -LL | | "{}", -LL | | // comment with a comma , in it -LL | | val, -LL | | ); - | |_____^ - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:149:5 - | -LL | println!("{}", /* comment with a comma , in it */ val); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{}", /* comment with a comma , in it */ val); -LL + println!("{val}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:155:9 - | -LL | panic!("p1 {}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - panic!("p1 {}", local_i32); -LL + panic!("p1 {local_i32}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:158:9 - | -LL | panic!("p2 {0}", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - panic!("p2 {0}", local_i32); -LL + panic!("p2 {local_i32}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:161:9 - | -LL | panic!("p3 {local_i32}", local_i32 = local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - panic!("p3 {local_i32}", local_i32 = local_i32); -LL + panic!("p3 {local_i32}"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:181:5 - | -LL | println!("expand='{}'", local_i32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("expand='{}'", local_i32); -LL + println!("expand='{local_i32}'"); - | - -error: aborting due to 76 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index 05ed5b6616c0..a12abf8bef8a 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -177,42 +177,6 @@ LL - println!("{:.1}", local_f64); LL + println!("{local_f64:.1}"); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:59:5 - | -LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); -LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:60:5 - | -LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64); -LL + println!("Hello {local_i32} is {local_f64:.*}", 5); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:61:5 - | -LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64); -LL + println!("Hello {local_i32} is {local_f64:.*}", 5); - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:62:5 | @@ -225,18 +189,6 @@ LL - println!("{} {}", local_i32, local_f64); LL + println!("{local_i32} {local_f64}"); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:63:5 - | -LL | println!("{}, {}", local_i32, local_opt.unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{}, {}", local_i32, local_opt.unwrap()); -LL + println!("{local_i32}, {}", local_opt.unwrap()); - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:64:5 | @@ -904,5 +856,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 76 previous errors +error: aborting due to 72 previous errors diff --git a/tests/ui/uninlined_format_args_panic.edition2018.stderr b/tests/ui/uninlined_format_args_panic.edition2018.stderr index 1afdb4d0fbab..2c8061259229 100644 --- a/tests/ui/uninlined_format_args_panic.edition2018.stderr +++ b/tests/ui/uninlined_format_args_panic.edition2018.stderr @@ -4,7 +4,6 @@ error: variables can be used directly in the `format!` string LL | println!("val='{}'", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this lint can also fix mixed format arg inlining if `allow-mixed-uninlined-format-args = false` is set in the `clippy.toml` file = note: `-D clippy::uninlined-format-args` implied by `-D warnings` help: change this to | diff --git a/tests/ui/uninlined_format_args_panic.edition2021.stderr b/tests/ui/uninlined_format_args_panic.edition2021.stderr index a38ea4168bc9..0f09c45f4132 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.stderr +++ b/tests/ui/uninlined_format_args_panic.edition2021.stderr @@ -4,7 +4,6 @@ error: variables can be used directly in the `format!` string LL | println!("val='{}'", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: this lint can also fix mixed format arg inlining if `allow-mixed-uninlined-format-args = false` is set in the `clippy.toml` file = note: `-D clippy::uninlined-format-args` implied by `-D warnings` help: change this to | From 5011c675adf627e9c2a6e226fa9b504ad740a554 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 23 Nov 2022 15:39:42 +1100 Subject: [PATCH 279/524] Rename `ast::Lit` as `ast::MetaItemLit`. --- clippy_lints/src/attrs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index ecf8e83375db..018f10f25886 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -6,7 +6,7 @@ use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, @@ -576,7 +576,7 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut } } -fn check_semver(cx: &LateContext<'_>, span: Span, lit: &Lit) { +fn check_semver(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) { if let LitKind::Str(is, _) = lit.kind { if Version::parse(is.as_str()).is_ok() { return; From fc108d4b61cb56943c5e44d6d89d7418bfa6c1c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 28 Nov 2022 00:41:31 -0800 Subject: [PATCH 280/524] fix clippy tests --- tests/ui/async_yields_async.stderr | 20 ++++++++++---------- tests/ui/result_map_unit_fn_unfixable.stderr | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index b0c4215e7ddf..92ba35929678 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -2,14 +2,14 @@ error: an async construct yields a type which is itself awaitable --> $DIR/async_yields_async.rs:39:9 | LL | let _h = async { - | ____________________- -LL | | async { - | |_________^ + | _____________________- +LL | | async { + | | _________^ LL | || 3 LL | || } | ||_________^ awaitable value not awaited -LL | | }; - | |_____- outer async construct +LL | | }; + | |______- outer async construct | = note: `-D clippy::async-yields-async` implied by `-D warnings` help: consider awaiting this value @@ -36,14 +36,14 @@ error: an async construct yields a type which is itself awaitable --> $DIR/async_yields_async.rs:50:9 | LL | let _j = async || { - | _______________________- -LL | | async { - | |_________^ + | ________________________- +LL | | async { + | | _________^ LL | || 3 LL | || } | ||_________^ awaitable value not awaited -LL | | }; - | |_____- outer async construct +LL | | }; + | |______- outer async construct | help: consider awaiting this value | diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index 88e4efdb0f05..2e1eb8eb1806 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -20,14 +20,14 @@ error: called `map(f)` on an `Result` value where `f` is a closure that returns --> $DIR/result_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| { - | _____^ - | |_____| + | ______^ + | | _____| | || LL | || do_nothing(value); LL | || do_nothing(value) LL | || }); | ||______^- help: try this: `if let Ok(value) = x.field { ... }` - | |_______| + | |______| | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` From f62eab43123ac463d7ff95590c285cb30e86b1f7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 25 Nov 2022 17:10:05 +0100 Subject: [PATCH 281/524] Adjust description once more --- clippy_lints/src/semicolon_block.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 6861b3709394..d3cab68137c4 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -8,7 +8,7 @@ use rustc_span::Span; declare_clippy_lint! { /// ### What it does /// - /// Checks for semicolon terminated blocks containing only a single expression. + /// Suggests moving the semicolon from a block inside of the block to its kast expression. /// /// ### Why is this bad? /// @@ -36,7 +36,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// - /// Checks for blocks containing only a single semicolon terminated statement. + /// Suggests moving the semicolon from a block's final expression outside of the block. /// /// ### Why is this bad? /// @@ -80,7 +80,7 @@ impl LateLintPass<'_> for SemicolonBlock { span, .. } = stmt else { return }; - semicolon_outside_block(cx, block, expr, span) + semicolon_outside_block(cx, block, expr, span); }, StmtKind::Semi(Expr { kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _), From 82c07f09e52379c8388abd8ca5ea6df246f686b1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 25 Nov 2022 17:11:15 +0000 Subject: [PATCH 282/524] partially_normalize_... -> At::normalize --- clippy_utils/src/ty.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index f4459e3e6633..82496f120e37 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -22,6 +22,7 @@ use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Size, VariantIdx}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::NormalizeExt; use rustc_trait_selection::traits::query::normalize::AtExt; use std::iter; From a61e2a91f5071719f3ac4696ee3061c171a47798 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 25 Nov 2022 17:28:50 +0000 Subject: [PATCH 283/524] FnCtxt normalization stuff --- clippy_utils/src/ty.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 82496f120e37..2ceda3511fe4 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -22,8 +22,7 @@ use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Size, VariantIdx}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::NormalizeExt; -use rustc_trait_selection::traits::query::normalize::AtExt; +use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use std::iter; use crate::{match_def_path, path_res, paths}; @@ -284,7 +283,7 @@ fn is_normalizable_helper<'tcx>( cache.insert(ty, false); let infcx = cx.tcx.infer_ctxt().build(); let cause = rustc_middle::traits::ObligationCause::dummy(); - let result = if infcx.at(&cause, param_env).normalize(ty).is_ok() { + let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() { match ty.kind() { ty::Adt(def, substs) => def.variants().iter().all(|variant| { variant From 6bbf16660bf218b16d64d812b4c3d916cf827a88 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 28 Nov 2022 20:23:09 +0100 Subject: [PATCH 284/524] Move `index_refutable_slice` to `pedantic` --- clippy_lints/src/index_refutable_slice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index ee5f10da5b84..cf35b1f175c6 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -47,7 +47,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.59.0"] pub INDEX_REFUTABLE_SLICE, - nursery, + pedantic, "avoid indexing on slices which could be destructed" } From 0893322e5412f78e8214c3ebc8b5b79a8c976f38 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 28 Nov 2022 23:56:02 -0500 Subject: [PATCH 285/524] Don't lint `unnecessary_operation` in mixed macro contexts --- clippy_lints/src/no_effect.rs | 8 ++++++-- tests/ui/unnecessary_operation.fixed | 9 +++++++++ tests/ui/unnecessary_operation.rs | 9 +++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 819646bb6780..79c1ae4861e8 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -6,7 +6,8 @@ use clippy_utils::ty::has_drop; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::ops::Deref; @@ -159,8 +160,11 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if_chain! { if let StmtKind::Semi(expr) = stmt.kind; + let ctxt = stmt.span.ctxt(); + if expr.span.ctxt() == ctxt; if let Some(reduced) = reduce_expression(cx, expr); - if !&reduced.iter().any(|e| e.span.from_expansion()); + if !in_external_macro(cx.sess(), stmt.span); + if reduced.iter().all(|e| e.span.ctxt() == ctxt); then { if let ExprKind::Index(..) = &expr.kind { let snippet = if let (Some(arr), Some(func)) = diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index bf0ec8deb345..d37163570abe 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -76,4 +76,13 @@ fn main() { DropStruct { ..get_drop_struct() }; DropEnum::Tuple(get_number()); DropEnum::Struct { field: get_number() }; + + // Issue #9954 + fn one() -> i8 { + 1 + } + macro_rules! use_expr { + ($($e:expr),*) => {{ $($e;)* }} + } + use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one()); } diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index 08cb9ab522ee..a14fd4bca0ef 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -80,4 +80,13 @@ fn main() { DropStruct { ..get_drop_struct() }; DropEnum::Tuple(get_number()); DropEnum::Struct { field: get_number() }; + + // Issue #9954 + fn one() -> i8 { + 1 + } + macro_rules! use_expr { + ($($e:expr),*) => {{ $($e;)* }} + } + use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one()); } From f44b7aa81eca5916a165f571b4621a9ec1dd22fb Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 28 Nov 2022 23:30:56 -0500 Subject: [PATCH 286/524] Don't lint `unnecessary_cast` in mixed macro context --- clippy_lints/src/casts/unnecessary_cast.rs | 11 +++++- tests/ui/unnecessary_cast.fixed | 11 ++++++ tests/ui/unnecessary_cast.rs | 11 ++++++ tests/ui/unnecessary_cast.stderr | 44 +++++++++++----------- 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index c8596987e4d7..ecc8a8de97b9 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet_opt; +use clippy_utils::{get_parent_expr, path_to_local}; use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; @@ -75,6 +75,15 @@ pub(super) fn check<'tcx>( } if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { + if let Some(id) = path_to_local(cast_expr) + && let Some(span) = cx.tcx.hir().opt_span(id) + && span.ctxt() != cast_expr.span.ctxt() + { + // Binding context is different than the identifiers context. + // Weird macro wizardry could be involved here. + return false; + } + span_lint_and_sugg( cx, UNNECESSARY_CAST, diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index ec8c6abfab91..f234d4473c3e 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -41,6 +41,17 @@ fn main() { // do not lint cast to alias type 1 as I32Alias; &1 as &I32Alias; + + // issue #9960 + macro_rules! bind_var { + ($id:ident, $e:expr) => {{ + let $id = 0usize; + let _ = $e != 0usize; + let $id = 0isize; + let _ = $e != 0usize; + }} + } + bind_var!(x, (x as usize) + 1); } type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 5213cdc269bd..855a4efa0341 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -41,6 +41,17 @@ fn main() { // do not lint cast to alias type 1 as I32Alias; &1 as &I32Alias; + + // issue #9960 + macro_rules! bind_var { + ($id:ident, $e:expr) => {{ + let $id = 0usize; + let _ = $e != 0usize; + let $id = 0isize; + let _ = $e != 0usize; + }} + } + bind_var!(x, (x as usize) + 1); } type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index e5c3dd5e53f8..934db0e86e66 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -49,133 +49,133 @@ LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:53:9 + --> $DIR/unnecessary_cast.rs:64:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:54:9 + --> $DIR/unnecessary_cast.rs:65:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:55:9 + --> $DIR/unnecessary_cast.rs:66:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:56:17 + --> $DIR/unnecessary_cast.rs:67:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:57:17 + --> $DIR/unnecessary_cast.rs:68:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:58:17 + --> $DIR/unnecessary_cast.rs:69:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:59:9 + --> $DIR/unnecessary_cast.rs:70:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:60:9 + --> $DIR/unnecessary_cast.rs:71:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast.rs:72:9 + --> $DIR/unnecessary_cast.rs:83:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:73:9 + --> $DIR/unnecessary_cast.rs:84:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast.rs:74:9 + --> $DIR/unnecessary_cast.rs:85:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast.rs:75:9 + --> $DIR/unnecessary_cast.rs:86:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast.rs:76:9 + --> $DIR/unnecessary_cast.rs:87:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:78:9 + --> $DIR/unnecessary_cast.rs:89:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:79:9 + --> $DIR/unnecessary_cast.rs:90:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:83:17 + --> $DIR/unnecessary_cast.rs:94:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:84:17 + --> $DIR/unnecessary_cast.rs:95:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:93:22 + --> $DIR/unnecessary_cast.rs:104:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> $DIR/unnecessary_cast.rs:95:22 + --> $DIR/unnecessary_cast.rs:106:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:102:22 + --> $DIR/unnecessary_cast.rs:113:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:104:23 + --> $DIR/unnecessary_cast.rs:115:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> $DIR/unnecessary_cast.rs:112:20 + --> $DIR/unnecessary_cast.rs:123:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` From 96f1385fdd75af35ea888eeb68dc3631d7637a2b Mon Sep 17 00:00:00 2001 From: naosense Date: Mon, 21 Nov 2022 14:36:56 +0800 Subject: [PATCH 287/524] add `suppress_lint_in_const` conf --- clippy_lints/src/indexing_slicing.rs | 19 ++++++++++++++++--- clippy_lints/src/lib.rs | 3 +++ clippy_lints/src/utils/conf.rs | 4 ++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 4cd7dff4cfd7..23beeb4458c7 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -82,11 +82,24 @@ declare_clippy_lint! { "indexing/slicing usage" } +#[derive(Copy, Clone)] +pub struct IndexingSlicing { + suppress_lint_in_const: bool, +} + +impl IndexingSlicing { + pub fn new(suppress_lint_in_const: bool) -> Self { + Self { + suppress_lint_in_const, + } + } +} + declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + if self.suppress_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) { return; } @@ -146,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind() { // Index is a const block. - if let ExprKind::ConstBlock(..) = index.kind { + if self.suppress_lint_in_const && let ExprKind::ConstBlock(..) = index.kind { return; } // Index is a constant uint. @@ -191,7 +204,7 @@ fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u1 } else { Some(x) } - }, + } Some(_) => None, None => Some(array_size), }; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3fe39488ab82..3bac394582e0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -562,7 +562,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; let allow_expect_in_tests = conf.allow_expect_in_tests; let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; + let suppress_lint_in_const = conf.suppress_lint_in_const; store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); + store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv))); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, @@ -684,6 +686,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); store.register_late_pass(|_| Box::new(unwrap::Unwrap)); store.register_late_pass(|_| Box::new(indexing_slicing::IndexingSlicing)); + store.register_late_pass(|_| Box::new(indexing_slicing::IndexingSlicing::new(suppress_lint_in_const))); store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst)); store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b6dc8cd7ab11..1352632b3a65 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -406,6 +406,10 @@ define_Conf! { /// /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` (allow_mixed_uninlined_format_args: bool = true), + /// Lint: SUPPRESS_LINT + /// + /// Whether to suppress lint in const function + (suppress_lint_in_const: bool = true), } /// Search for the configuration file. From aed9497978ce0520147cf1cf100513bf049ddf50 Mon Sep 17 00:00:00 2001 From: naosense Date: Tue, 22 Nov 2022 17:12:50 +0800 Subject: [PATCH 288/524] add test and stderr --- clippy_lints/src/indexing_slicing.rs | 12 +++++------- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/utils/conf.rs | 2 +- tests/ui-toml/suppress_lint_in_const/clippy.toml | 1 + tests/ui-toml/suppress_lint_in_const/test.rs | 15 +++++++++++++++ tests/ui-toml/suppress_lint_in_const/test.stderr | 11 +++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 1 + 7 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 tests/ui-toml/suppress_lint_in_const/clippy.toml create mode 100644 tests/ui-toml/suppress_lint_in_const/test.rs create mode 100644 tests/ui-toml/suppress_lint_in_const/test.stderr diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 23beeb4458c7..92facbbf0341 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -7,7 +7,7 @@ use rustc_ast::ast::RangeLimits; 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_tool_lint, impl_lint_pass}; declare_clippy_lint! { /// ### What it does @@ -82,6 +82,8 @@ declare_clippy_lint! { "indexing/slicing usage" } +impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); + #[derive(Copy, Clone)] pub struct IndexingSlicing { suppress_lint_in_const: bool, @@ -89,14 +91,10 @@ pub struct IndexingSlicing { impl IndexingSlicing { pub fn new(suppress_lint_in_const: bool) -> Self { - Self { - suppress_lint_in_const, - } + Self { suppress_lint_in_const } } } -declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); - impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if self.suppress_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) { @@ -204,7 +202,7 @@ fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u1 } else { Some(x) } - } + }, Some(_) => None, None => Some(array_size), }; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3bac394582e0..3b5bac644ef5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -685,8 +685,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); store.register_late_pass(|_| Box::new(unwrap::Unwrap)); - store.register_late_pass(|_| Box::new(indexing_slicing::IndexingSlicing)); - store.register_late_pass(|_| Box::new(indexing_slicing::IndexingSlicing::new(suppress_lint_in_const))); + store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(suppress_lint_in_const))); store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst)); store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 1352632b3a65..4a0e135db835 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -406,7 +406,7 @@ define_Conf! { /// /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` (allow_mixed_uninlined_format_args: bool = true), - /// Lint: SUPPRESS_LINT + /// Lint: INDEXING_SLICING /// /// Whether to suppress lint in const function (suppress_lint_in_const: bool = true), diff --git a/tests/ui-toml/suppress_lint_in_const/clippy.toml b/tests/ui-toml/suppress_lint_in_const/clippy.toml new file mode 100644 index 000000000000..fd459ff5ac54 --- /dev/null +++ b/tests/ui-toml/suppress_lint_in_const/clippy.toml @@ -0,0 +1 @@ +suppress-lint-in-const = false \ No newline at end of file diff --git a/tests/ui-toml/suppress_lint_in_const/test.rs b/tests/ui-toml/suppress_lint_in_const/test.rs new file mode 100644 index 000000000000..e5f4ca7cc902 --- /dev/null +++ b/tests/ui-toml/suppress_lint_in_const/test.rs @@ -0,0 +1,15 @@ +#![warn(clippy::indexing_slicing)] + +/// An opaque integer representation +pub struct Integer<'a> { + /// The underlying data + value: &'a [u8], +} +impl<'a> Integer<'a> { + // Check whether `self` holds a negative number or not + pub const fn is_negative(&self) -> bool { + self.value[0] & 0b1000_0000 != 0 + } +} + +fn main() {} diff --git a/tests/ui-toml/suppress_lint_in_const/test.stderr b/tests/ui-toml/suppress_lint_in_const/test.stderr new file mode 100644 index 000000000000..b4f6fe0c024b --- /dev/null +++ b/tests/ui-toml/suppress_lint_in_const/test.stderr @@ -0,0 +1,11 @@ +error: indexing may panic + --> $DIR/test.rs:11:9 + | +LL | self.value[0] & 0b1000_0000 != 0 + | ^^^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 01a5e962c949..521af13fe03d 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -35,6 +35,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie pass-by-value-size-limit single-char-binding-names-threshold standard-macro-braces + suppress-lint-in-const third-party too-large-for-stack too-many-arguments-threshold From 4528aec7e9a4fdce65a5dfec3a06aad5ee1950dd Mon Sep 17 00:00:00 2001 From: naosense Date: Wed, 23 Nov 2022 16:26:25 +0800 Subject: [PATCH 289/524] update config and suggest --- clippy_lints/src/indexing_slicing.rs | 45 ++++++++++++------ clippy_lints/src/lib.rs | 8 +++- clippy_lints/src/utils/conf.rs | 8 +++- .../suppress_lint_in_const/clippy.toml | 2 +- .../suppress_lint_in_const/test.stderr | 3 +- .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/indexing_slicing_index.stderr | 23 +++------ tests/ui/indexing_slicing_slice.stderr | 47 +++++-------------- 8 files changed, 63 insertions(+), 75 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 92facbbf0341..dfea0bf18d10 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,9 +1,10 @@ //! lint on indexing and slicing operations use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher; use rustc_ast::ast::RangeLimits; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -86,18 +87,20 @@ impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); #[derive(Copy, Clone)] pub struct IndexingSlicing { - suppress_lint_in_const: bool, + suppress_restriction_lint_in_const: bool, } impl IndexingSlicing { - pub fn new(suppress_lint_in_const: bool) -> Self { - Self { suppress_lint_in_const } + pub fn new(suppress_restriction_lint_in_const: bool) -> Self { + Self { + suppress_restriction_lint_in_const, + } } } impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if self.suppress_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) { + if self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) { return; } @@ -152,12 +155,19 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { (None, None) => return, // [..] is ok. }; - span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic", None, help_msg); + span_lint_and_then(cx, INDEXING_SLICING, expr.span, "slicing may panic", |diag| { + let note = if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + "the suggestion might not be applicable in constant blocks" + } else { + "" + }; + diag.span_suggestion(expr.span, help_msg, note, Applicability::MachineApplicable); + }); } else { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind() { // Index is a const block. - if self.suppress_lint_in_const && let ExprKind::ConstBlock(..) = index.kind { + if self.suppress_restriction_lint_in_const && let ExprKind::ConstBlock(..) = index.kind { return; } // Index is a constant uint. @@ -167,14 +177,19 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } } - span_lint_and_help( - cx, - INDEXING_SLICING, - expr.span, - "indexing may panic", - None, - "consider using `.get(n)` or `.get_mut(n)` instead", - ); + span_lint_and_then(cx, INDEXING_SLICING, expr.span, "indexing may panic", |diag| { + let note = if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + "the suggestion might not be applicable in constant blocks" + } else { + "" + }; + diag.span_suggestion( + expr.span, + "consider using `.get(n)` or `.get_mut(n)` instead", + note, + Applicability::MachineApplicable, + ); + }); } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3b5bac644ef5..2dc3a0822774 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -562,7 +562,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; let allow_expect_in_tests = conf.allow_expect_in_tests; let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; - let suppress_lint_in_const = conf.suppress_lint_in_const; + let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const; store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv))); store.register_late_pass(move |_| { @@ -685,7 +685,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); store.register_late_pass(|_| Box::new(unwrap::Unwrap)); - store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(suppress_lint_in_const))); + store.register_late_pass(move |_| { + Box::new(indexing_slicing::IndexingSlicing::new( + suppress_restriction_lint_in_const, + )) + }); store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst)); store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 4a0e135db835..0022044ea417 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -408,8 +408,12 @@ define_Conf! { (allow_mixed_uninlined_format_args: bool = true), /// Lint: INDEXING_SLICING /// - /// Whether to suppress lint in const function - (suppress_lint_in_const: bool = true), + /// Whether to suppress a restriction lint in constant code. In same + /// cases the restructured operation might not be unavoidable, as the + /// suggested counterparts are unavailable in constant code. This + /// configuration will cause restriction lints to trigger even + /// if no suggestion can be made. + (suppress_restriction_lint_in_const: bool = true), } /// Search for the configuration file. diff --git a/tests/ui-toml/suppress_lint_in_const/clippy.toml b/tests/ui-toml/suppress_lint_in_const/clippy.toml index fd459ff5ac54..d458f53a73dd 100644 --- a/tests/ui-toml/suppress_lint_in_const/clippy.toml +++ b/tests/ui-toml/suppress_lint_in_const/clippy.toml @@ -1 +1 @@ -suppress-lint-in-const = false \ No newline at end of file +suppress-restriction-lint-in-const = false \ No newline at end of file diff --git a/tests/ui-toml/suppress_lint_in_const/test.stderr b/tests/ui-toml/suppress_lint_in_const/test.stderr index b4f6fe0c024b..4e4583ab33cf 100644 --- a/tests/ui-toml/suppress_lint_in_const/test.stderr +++ b/tests/ui-toml/suppress_lint_in_const/test.stderr @@ -2,9 +2,8 @@ error: indexing may panic --> $DIR/test.rs:11:9 | LL | self.value[0] & 0b1000_0000 != 0 - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead: `the suggestion might not be applicable in constant blocks` | - = help: consider using `.get(n)` or `.get_mut(n)` instead = note: `-D clippy::indexing-slicing` implied by `-D warnings` error: aborting due to previous error diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 521af13fe03d..6ef2abb15b28 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -35,7 +35,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie pass-by-value-size-limit single-char-binding-names-threshold standard-macro-braces - suppress-lint-in-const + suppress-restriction-lint-in-const third-party too-large-for-stack too-many-arguments-threshold diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index d8b6e3f1262b..30fb69907958 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -14,50 +14,39 @@ error: indexing may panic --> $DIR/indexing_slicing_index.rs:22:5 | LL | x[index]; - | ^^^^^^^^ + | ^^^^^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead | - = help: consider using `.get(n)` or `.get_mut(n)` instead = note: `-D clippy::indexing-slicing` implied by `-D warnings` error: indexing may panic --> $DIR/indexing_slicing_index.rs:38:5 | LL | v[0]; - | ^^^^ - | - = help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic --> $DIR/indexing_slicing_index.rs:39:5 | LL | v[10]; - | ^^^^^ - | - = help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic --> $DIR/indexing_slicing_index.rs:40:5 | LL | v[1 << 3]; - | ^^^^^^^^^ - | - = help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^^^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic --> $DIR/indexing_slicing_index.rs:46:5 | LL | v[N]; - | ^^^^ - | - = help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic --> $DIR/indexing_slicing_index.rs:47:5 | LL | v[M]; - | ^^^^ - | - = help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead error[E0080]: evaluation of constant value failed --> $DIR/indexing_slicing_index.rs:10:24 diff --git a/tests/ui/indexing_slicing_slice.stderr b/tests/ui/indexing_slicing_slice.stderr index dc54bd41365d..ac2cc009a22a 100644 --- a/tests/ui/indexing_slicing_slice.stderr +++ b/tests/ui/indexing_slicing_slice.stderr @@ -2,50 +2,39 @@ error: slicing may panic --> $DIR/indexing_slicing_slice.rs:12:6 | LL | &x[index..]; - | ^^^^^^^^^^ + | ^^^^^^^^^^ help: consider using `.get(n..)` or .get_mut(n..)` instead | - = help: consider using `.get(n..)` or .get_mut(n..)` instead = note: `-D clippy::indexing-slicing` implied by `-D warnings` error: slicing may panic --> $DIR/indexing_slicing_slice.rs:13:6 | LL | &x[..index]; - | ^^^^^^^^^^ - | - = help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:14:6 | LL | &x[index_from..index_to]; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:15:6 | LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:15:6 | LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. - | ^^^^^^^^^^^^^^^ - | - = help: consider using `.get(n..)` or .get_mut(n..)` instead + | ^^^^^^^^^^^^^^^ help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:16:6 | LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10]. - | ^^^^^^^^^^^^ - | - = help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:16:8 @@ -59,17 +48,13 @@ error: slicing may panic --> $DIR/indexing_slicing_slice.rs:17:6 | LL | &x[0..][..3]; - | ^^^^^^^^^^^ - | - = help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:18:6 | LL | &x[1..][..5]; - | ^^^^^^^^^^^ - | - = help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:25:12 @@ -87,17 +72,13 @@ error: slicing may panic --> $DIR/indexing_slicing_slice.rs:31:6 | LL | &v[10..100]; - | ^^^^^^^^^^ - | - = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead + | ^^^^^^^^^^ help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:32:6 | LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. - | ^^^^^^^^^^^^^^ - | - = help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:32:8 @@ -109,17 +90,13 @@ error: slicing may panic --> $DIR/indexing_slicing_slice.rs:33:6 | LL | &v[10..]; - | ^^^^^^^ - | - = help: consider using `.get(n..)` or .get_mut(n..)` instead + | ^^^^^^^ help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:34:6 | LL | &v[..100]; - | ^^^^^^^^ - | - = help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead error: aborting due to 16 previous errors From c9bf4b75cea2259ad927228c762d4cf323628ae5 Mon Sep 17 00:00:00 2001 From: naosense Date: Wed, 23 Nov 2022 16:43:42 +0800 Subject: [PATCH 290/524] resolve conflicts --- clippy_lints/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2dc3a0822774..5ce70de88104 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -564,7 +564,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const; store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); - store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv))); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, From 1fc98c51dfd771e5cd45c2feee0649ae299ab18b Mon Sep 17 00:00:00 2001 From: naosense Date: Wed, 23 Nov 2022 16:50:05 +0800 Subject: [PATCH 291/524] change default value --- clippy_lints/src/utils/conf.rs | 2 +- tests/ui-toml/suppress_lint_in_const/clippy.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0022044ea417..f5f0e3ef48cf 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -413,7 +413,7 @@ define_Conf! { /// suggested counterparts are unavailable in constant code. This /// configuration will cause restriction lints to trigger even /// if no suggestion can be made. - (suppress_restriction_lint_in_const: bool = true), + (suppress_restriction_lint_in_const: bool = false), } /// Search for the configuration file. diff --git a/tests/ui-toml/suppress_lint_in_const/clippy.toml b/tests/ui-toml/suppress_lint_in_const/clippy.toml index d458f53a73dd..d6b6fc7f2688 100644 --- a/tests/ui-toml/suppress_lint_in_const/clippy.toml +++ b/tests/ui-toml/suppress_lint_in_const/clippy.toml @@ -1 +1 @@ -suppress-restriction-lint-in-const = false \ No newline at end of file +suppress-restriction-lint-in-const = false From 67a94135cbce195078efcbf18ac2347a1d734e68 Mon Sep 17 00:00:00 2001 From: naosense Date: Thu, 24 Nov 2022 09:43:11 +0800 Subject: [PATCH 292/524] change note style --- clippy_lints/src/indexing_slicing.rs | 29 +++---- .../suppress_lint_in_const/test.stderr | 4 +- tests/ui/indexing_slicing_index.stderr | 79 +++++++++++++++++-- tests/ui/indexing_slicing_slice.stderr | 47 ++++++++--- 4 files changed, 120 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index dfea0bf18d10..1a8ac43ac894 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -4,7 +4,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher; use rustc_ast::ast::RangeLimits; -use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -105,6 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } if let ExprKind::Index(array, index) = &expr.kind { + let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::Range::hir(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] @@ -156,12 +156,11 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { }; span_lint_and_then(cx, INDEXING_SLICING, expr.span, "slicing may panic", |diag| { - let note = if cx.tcx.hir().is_inside_const_context(expr.hir_id) { - "the suggestion might not be applicable in constant blocks" - } else { - "" - }; - diag.span_suggestion(expr.span, help_msg, note, Applicability::MachineApplicable); + diag.help(help_msg); + + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + diag.note(note); + } }); } else { // Catchall non-range index, i.e., [n] or [n << m] @@ -178,17 +177,11 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } span_lint_and_then(cx, INDEXING_SLICING, expr.span, "indexing may panic", |diag| { - let note = if cx.tcx.hir().is_inside_const_context(expr.hir_id) { - "the suggestion might not be applicable in constant blocks" - } else { - "" - }; - diag.span_suggestion( - expr.span, - "consider using `.get(n)` or `.get_mut(n)` instead", - note, - Applicability::MachineApplicable, - ); + diag.help("consider using `.get(n)` or `.get_mut(n)` instead"); + + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + diag.note(note); + } }); } } diff --git a/tests/ui-toml/suppress_lint_in_const/test.stderr b/tests/ui-toml/suppress_lint_in_const/test.stderr index 4e4583ab33cf..c2acfed559d0 100644 --- a/tests/ui-toml/suppress_lint_in_const/test.stderr +++ b/tests/ui-toml/suppress_lint_in_const/test.stderr @@ -2,8 +2,10 @@ error: indexing may panic --> $DIR/test.rs:11:9 | LL | self.value[0] & 0b1000_0000 != 0 - | ^^^^^^^^^^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead: `the suggestion might not be applicable in constant blocks` + | ^^^^^^^^^^^^^ | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks = note: `-D clippy::indexing-slicing` implied by `-D warnings` error: aborting due to previous error diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 30fb69907958..84e1f65623c3 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,3 +1,22 @@ +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:9:20 + | +LL | const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. + | ^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:10:24 + | +LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks + error[E0080]: evaluation of `main::{constant#3}` failed --> $DIR/indexing_slicing_index.rs:31:14 | @@ -14,39 +33,83 @@ error: indexing may panic --> $DIR/indexing_slicing_index.rs:22:5 | LL | x[index]; - | ^^^^^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^^^^^ | - = note: `-D clippy::indexing-slicing` implied by `-D warnings` + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:28:5 + | +LL | x[const { idx() }]; // Ok, should not produce stderr. + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:29:5 + | +LL | x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:30:14 + | +LL | const { &ARR[idx()] }; // Ok, should not produce stderr. + | ^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:31:14 + | +LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks error: indexing may panic --> $DIR/indexing_slicing_index.rs:38:5 | LL | v[0]; - | ^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic --> $DIR/indexing_slicing_index.rs:39:5 | LL | v[10]; - | ^^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic --> $DIR/indexing_slicing_index.rs:40:5 | LL | v[1 << 3]; - | ^^^^^^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic --> $DIR/indexing_slicing_index.rs:46:5 | LL | v[N]; - | ^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic --> $DIR/indexing_slicing_index.rs:47:5 | LL | v[M]; - | ^^^^ help: consider using `.get(n)` or `.get_mut(n)` instead + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead error[E0080]: evaluation of constant value failed --> $DIR/indexing_slicing_index.rs:10:24 @@ -54,6 +117,6 @@ error[E0080]: evaluation of constant value failed LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -error: aborting due to 8 previous errors +error: aborting due to 14 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/indexing_slicing_slice.stderr b/tests/ui/indexing_slicing_slice.stderr index ac2cc009a22a..dc54bd41365d 100644 --- a/tests/ui/indexing_slicing_slice.stderr +++ b/tests/ui/indexing_slicing_slice.stderr @@ -2,39 +2,50 @@ error: slicing may panic --> $DIR/indexing_slicing_slice.rs:12:6 | LL | &x[index..]; - | ^^^^^^^^^^ help: consider using `.get(n..)` or .get_mut(n..)` instead + | ^^^^^^^^^^ | + = help: consider using `.get(n..)` or .get_mut(n..)` instead = note: `-D clippy::indexing-slicing` implied by `-D warnings` error: slicing may panic --> $DIR/indexing_slicing_slice.rs:13:6 | LL | &x[..index]; - | ^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^ + | + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:14:6 | LL | &x[index_from..index_to]; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.get(n..m)` or `.get_mut(n..m)` instead + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:15:6 | LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:15:6 | LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. - | ^^^^^^^^^^^^^^^ help: consider using `.get(n..)` or .get_mut(n..)` instead + | ^^^^^^^^^^^^^^^ + | + = help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:16:6 | LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10]. - | ^^^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^^^ + | + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:16:8 @@ -48,13 +59,17 @@ error: slicing may panic --> $DIR/indexing_slicing_slice.rs:17:6 | LL | &x[0..][..3]; - | ^^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^^ + | + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:18:6 | LL | &x[1..][..5]; - | ^^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^^ + | + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:25:12 @@ -72,13 +87,17 @@ error: slicing may panic --> $DIR/indexing_slicing_slice.rs:31:6 | LL | &v[10..100]; - | ^^^^^^^^^^ help: consider using `.get(n..m)` or `.get_mut(n..m)` instead + | ^^^^^^^^^^ + | + = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:32:6 | LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. - | ^^^^^^^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^^^^^^^ + | + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:32:8 @@ -90,13 +109,17 @@ error: slicing may panic --> $DIR/indexing_slicing_slice.rs:33:6 | LL | &v[10..]; - | ^^^^^^^ help: consider using `.get(n..)` or .get_mut(n..)` instead + | ^^^^^^^ + | + = help: consider using `.get(n..)` or .get_mut(n..)` instead error: slicing may panic --> $DIR/indexing_slicing_slice.rs:34:6 | LL | &v[..100]; - | ^^^^^^^^ help: consider using `.get(..n)`or `.get_mut(..n)` instead + | ^^^^^^^^ + | + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: aborting due to 16 previous errors From 8759f33bb338e9d9a2ebe36ee3e11f4643acdc8a Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 29 Nov 2022 05:18:43 -0800 Subject: [PATCH 293/524] Add S-waiting-on-review autolabel. --- triagebot.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index c615d18f8687..acb476ee6962 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -9,6 +9,9 @@ allow-unauthenticated = [ # See https://github.com/rust-lang/triagebot/wiki/Shortcuts [shortcut] +[autolabel."S-waiting-on-review"] +new_pr = true + [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" From c502dee41ec38e6e0096abaa50a48e792e4c9d40 Mon Sep 17 00:00:00 2001 From: dswij Date: Wed, 30 Nov 2022 14:50:13 +0800 Subject: [PATCH 294/524] Use `snippet_with_context` instead of `_with_macro_callsite` --- clippy_lints/src/manual_let_else.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 91f0dc2a7170..874d36ca9f4e 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::peel_blocks; -use clippy_utils::source::{snippet, snippet_with_macro_callsite}; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{for_each_expr, Descend}; use if_chain::if_chain; @@ -141,16 +141,10 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: // * unused binding collision detection with existing ones // * putting patterns with at the top level | inside () // for this to be machine applicable. - let app = Applicability::HasPlaceholders; - - let snippet_fn = if span.from_expansion() { - snippet - } else { - snippet_with_macro_callsite - }; - let sn_pat = snippet_fn(cx, pat.span, ""); - let sn_expr = snippet_fn(cx, expr.span, ""); - let sn_else = snippet_fn(cx, else_body.span, ""); + let mut app = Applicability::HasPlaceholders; + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); + let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); + let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { sn_else.into_owned() From cc43c3d25d4c0a6cf3dc3df7d5b560656c652bab Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 29 Nov 2022 18:22:02 +0100 Subject: [PATCH 295/524] Move `unnecessary_unsafety_doc` to `pedantic` --- clippy_lints/src/doc.rs | 2 +- ...ssary_unsafe.rs => unnecessary_unsafety_doc.rs} | 1 + ...safe.stderr => unnecessary_unsafety_doc.stderr} | 14 +++++++------- 3 files changed, 9 insertions(+), 8 deletions(-) rename tests/ui/{doc_unnecessary_unsafe.rs => unnecessary_unsafety_doc.rs} (98%) rename tests/ui/{doc_unnecessary_unsafe.stderr => unnecessary_unsafety_doc.stderr} (81%) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 4557e4328854..49b5d8b60d8b 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -253,7 +253,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.66.0"] pub UNNECESSARY_SAFETY_DOC, - style, + restriction, "`pub fn` or `pub trait` with `# Safety` docs" } diff --git a/tests/ui/doc_unnecessary_unsafe.rs b/tests/ui/unnecessary_unsafety_doc.rs similarity index 98% rename from tests/ui/doc_unnecessary_unsafe.rs rename to tests/ui/unnecessary_unsafety_doc.rs index d9e9363b0f4b..c160e31afd33 100644 --- a/tests/ui/doc_unnecessary_unsafe.rs +++ b/tests/ui/unnecessary_unsafety_doc.rs @@ -1,6 +1,7 @@ // aux-build:doc_unsafe_macros.rs #![allow(clippy::let_unit_value)] +#![warn(clippy::unnecessary_safety_doc)] #[macro_use] extern crate doc_unsafe_macros; diff --git a/tests/ui/doc_unnecessary_unsafe.stderr b/tests/ui/unnecessary_unsafety_doc.stderr similarity index 81% rename from tests/ui/doc_unnecessary_unsafe.stderr rename to tests/ui/unnecessary_unsafety_doc.stderr index 83b2efbb346b..72898c93fa11 100644 --- a/tests/ui/doc_unnecessary_unsafe.stderr +++ b/tests/ui/unnecessary_unsafety_doc.stderr @@ -1,5 +1,5 @@ error: safe function's docs have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:18:1 + --> $DIR/unnecessary_unsafety_doc.rs:19:1 | LL | pub fn apocalypse(universe: &mut ()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,31 +7,31 @@ LL | pub fn apocalypse(universe: &mut ()) { = note: `-D clippy::unnecessary-safety-doc` implied by `-D warnings` error: safe function's docs have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:44:5 + --> $DIR/unnecessary_unsafety_doc.rs:45:5 | LL | pub fn republished() { | ^^^^^^^^^^^^^^^^^^^^ error: safe function's docs have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:57:5 + --> $DIR/unnecessary_unsafety_doc.rs:58:5 | LL | fn documented(self); | ^^^^^^^^^^^^^^^^^^^^ error: docs for safe trait have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:67:1 + --> $DIR/unnecessary_unsafety_doc.rs:68:1 | LL | pub trait DocumentedSafeTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: safe function's docs have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:95:5 + --> $DIR/unnecessary_unsafety_doc.rs:96:5 | LL | pub fn documented() -> Self { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: safe function's docs have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:122:9 + --> $DIR/unnecessary_unsafety_doc.rs:123:9 | LL | pub fn drive() { | ^^^^^^^^^^^^^^ @@ -42,7 +42,7 @@ LL | very_safe!(); = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for safe trait have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:146:1 + --> $DIR/unnecessary_unsafety_doc.rs:147:1 | LL | pub trait DocumentedSafeTraitWithImplementationHeader { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 7a0f0c0e8b79d0dd95e0c97660e327e51fa09c0f Mon Sep 17 00:00:00 2001 From: mdgaziur Date: Sat, 26 Nov 2022 12:17:49 +0600 Subject: [PATCH 296/524] Fix #9958 --- clippy_lints/src/len_zero.rs | 20 +++++++--- tests/ui/len_zero.fixed | 40 +++++++++++++++++++ tests/ui/len_zero.rs | 40 +++++++++++++++++++ tests/ui/len_zero.stderr | 74 ++++++++++++++++++++++++++++-------- 4 files changed, 153 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 4c133c06a157..3babc2a58959 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed}; +use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefIdSet; use rustc_hir::{ def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item, - ItemKind, Mutability, Node, TraitItemRef, TyKind, + ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -16,6 +16,7 @@ use rustc_span::{ source_map::{Span, Spanned, Symbol}, symbol::sym, }; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -428,16 +429,23 @@ fn check_len( fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) { if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { let mut applicability = Applicability::MachineApplicable; + + let lit1 = peel_ref_operators(cx, lit1); + let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability); + + // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will + // cause the code to dereference boolean(won't compile). + if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind { + lit_str = Cow::from(format!("({lit_str})")); + } + span_lint_and_sugg( cx, COMPARISON_TO_EMPTY, span, "comparison to empty slice", &format!("using `{op}is_empty` is clearer and more explicit"), - format!( - "{op}{}.is_empty()", - snippet_with_applicability(cx, lit1.span, "_", &mut applicability) - ), + format!("{op}{lit_str}.is_empty()"), applicability, ); } diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index 1f3b8ac99b19..c1c0b5ae40f6 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -3,6 +3,9 @@ #![warn(clippy::len_zero)] #![allow(dead_code, unused, clippy::len_without_is_empty)] +extern crate core; +use core::ops::Deref; + pub struct One; struct Wither; @@ -56,6 +59,26 @@ impl WithIsEmpty for Wither { } } +struct DerefToDerefToString; + +impl Deref for DerefToDerefToString { + type Target = DerefToString; + + fn deref(&self) -> &Self::Target { + &DerefToString {} + } +} + +struct DerefToString; + +impl Deref for DerefToString { + type Target = str; + + fn deref(&self) -> &Self::Target { + "Hello, world!" + } +} + fn main() { let x = [1, 2]; if x.is_empty() { @@ -64,6 +87,23 @@ fn main() { if "".is_empty() {} + let s = "Hello, world!"; + let s1 = &s; + let s2 = &s1; + let s3 = &s2; + let s4 = &s3; + let s5 = &s4; + let s6 = &s5; + println!("{}", s1.is_empty()); + println!("{}", s2.is_empty()); + println!("{}", s3.is_empty()); + println!("{}", s4.is_empty()); + println!("{}", s5.is_empty()); + println!("{}", (s6).is_empty()); + + let d2s = DerefToDerefToString {}; + println!("{}", (**d2s).is_empty()); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index dc21de0001b6..cc2eb05b6bfd 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -3,6 +3,9 @@ #![warn(clippy::len_zero)] #![allow(dead_code, unused, clippy::len_without_is_empty)] +extern crate core; +use core::ops::Deref; + pub struct One; struct Wither; @@ -56,6 +59,26 @@ impl WithIsEmpty for Wither { } } +struct DerefToDerefToString; + +impl Deref for DerefToDerefToString { + type Target = DerefToString; + + fn deref(&self) -> &Self::Target { + &DerefToString {} + } +} + +struct DerefToString; + +impl Deref for DerefToString { + type Target = str; + + fn deref(&self) -> &Self::Target { + "Hello, world!" + } +} + fn main() { let x = [1, 2]; if x.len() == 0 { @@ -64,6 +87,23 @@ fn main() { if "".len() == 0 {} + let s = "Hello, world!"; + let s1 = &s; + let s2 = &s1; + let s3 = &s2; + let s4 = &s3; + let s5 = &s4; + let s6 = &s5; + println!("{}", *s1 == ""); + println!("{}", **s2 == ""); + println!("{}", ***s3 == ""); + println!("{}", ****s4 == ""); + println!("{}", *****s5 == ""); + println!("{}", ******(s6) == ""); + + let d2s = DerefToDerefToString {}; + println!("{}", &**d2s == ""); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. diff --git a/tests/ui/len_zero.stderr b/tests/ui/len_zero.stderr index 6c71f1beeac6..b6f13780253c 100644 --- a/tests/ui/len_zero.stderr +++ b/tests/ui/len_zero.stderr @@ -1,5 +1,5 @@ error: length comparison to zero - --> $DIR/len_zero.rs:61:8 + --> $DIR/len_zero.rs:84:8 | LL | if x.len() == 0 { | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()` @@ -7,82 +7,126 @@ LL | if x.len() == 0 { = note: `-D clippy::len-zero` implied by `-D warnings` error: length comparison to zero - --> $DIR/len_zero.rs:65:8 + --> $DIR/len_zero.rs:88:8 | LL | if "".len() == 0 {} | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()` +error: comparison to empty slice + --> $DIR/len_zero.rs:97:20 + | +LL | println!("{}", *s1 == ""); + | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s1.is_empty()` + | + = note: `-D clippy::comparison-to-empty` implied by `-D warnings` + +error: comparison to empty slice + --> $DIR/len_zero.rs:98:20 + | +LL | println!("{}", **s2 == ""); + | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s2.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:99:20 + | +LL | println!("{}", ***s3 == ""); + | ^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s3.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:100:20 + | +LL | println!("{}", ****s4 == ""); + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s4.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:101:20 + | +LL | println!("{}", *****s5 == ""); + | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s5.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:102:20 + | +LL | println!("{}", ******(s6) == ""); + | ^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(s6).is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:105:20 + | +LL | println!("{}", &**d2s == ""); + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()` + error: length comparison to zero - --> $DIR/len_zero.rs:80:8 + --> $DIR/len_zero.rs:120:8 | LL | if has_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:83:8 + --> $DIR/len_zero.rs:123:8 | LL | if has_is_empty.len() != 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:86:8 + --> $DIR/len_zero.rs:126:8 | LL | if has_is_empty.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:89:8 + --> $DIR/len_zero.rs:129:8 | LL | if has_is_empty.len() < 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:92:8 + --> $DIR/len_zero.rs:132:8 | LL | if has_is_empty.len() >= 1 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:103:8 + --> $DIR/len_zero.rs:143:8 | LL | if 0 == has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:106:8 + --> $DIR/len_zero.rs:146:8 | LL | if 0 != has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:109:8 + --> $DIR/len_zero.rs:149:8 | LL | if 0 < has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:112:8 + --> $DIR/len_zero.rs:152:8 | LL | if 1 <= has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:115:8 + --> $DIR/len_zero.rs:155:8 | LL | if 1 > has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:129:8 + --> $DIR/len_zero.rs:169:8 | LL | if with_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:142:8 + --> $DIR/len_zero.rs:182:8 | LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` -error: aborting due to 14 previous errors +error: aborting due to 21 previous errors From e0eba9cafcc8aaf3821f4b0b9777954caf049498 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 29 Nov 2022 10:36:43 -0500 Subject: [PATCH 297/524] Don't cross contexts while building the suggestion for `redundant_closure_call` --- clippy_lints/src/collapsible_if.rs | 10 +-- clippy_lints/src/redundant_closure_call.rs | 4 +- clippy_utils/src/source.rs | 38 +++++++++-- clippy_utils/src/sugg.rs | 65 ++++++++++--------- tests/ui/redundant_closure_call_fixable.fixed | 12 ++++ tests/ui/redundant_closure_call_fixable.rs | 12 ++++ .../ui/redundant_closure_call_fixable.stderr | 24 ++++++- 7 files changed, 122 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 90430b71a0ef..b38e09dc09f4 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -160,11 +160,13 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind; // Prevent triggering on `if c { if let a = b { .. } }`. if !matches!(check_inner.kind, ast::ExprKind::Let(..)); - if expr.span.ctxt() == inner.span.ctxt(); + let ctxt = expr.span.ctxt(); + if inner.span.ctxt() == ctxt; then { span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| { - let lhs = Sugg::ast(cx, check, ".."); - let rhs = Sugg::ast(cx, check_inner, ".."); + let mut app = Applicability::MachineApplicable; + let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app); + let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app); diag.span_suggestion( expr.span, "collapse nested if block", @@ -173,7 +175,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & lhs.and(&rhs), snippet_block(cx, content.span, "..", Some(expr.span)), ), - Applicability::MachineApplicable, // snippet + app, // snippet ); }); } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 8e675d34a183..2a42e73488f1 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -81,8 +81,8 @@ impl EarlyLintPass for RedundantClosureCall { "try not to call a closure in the expression where it is declared", |diag| { if fn_decl.inputs.is_empty() { - let app = Applicability::MachineApplicable; - let mut hint = Sugg::ast(cx, body, ".."); + let mut app = Applicability::MachineApplicable; + let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app); if asyncness.is_async() { // `async x` is a syntax error, so it becomes `async { x }` diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index eacfa91ba556..cd5dcfdaca34 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; +use rustc_session::Session; use rustc_span::hygiene; use rustc_span::source_map::{original_sp, SourceMap}; use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; @@ -204,11 +205,20 @@ pub fn snippet_with_applicability<'a, T: LintContext>( span: Span, default: &'a str, applicability: &mut Applicability, +) -> Cow<'a, str> { + snippet_with_applicability_sess(cx.sess(), span, default, applicability) +} + +fn snippet_with_applicability_sess<'a>( + sess: &Session, + span: Span, + default: &'a str, + applicability: &mut Applicability, ) -> Cow<'a, str> { if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - snippet_opt(cx, span).map_or_else( + snippet_opt_sess(sess, span).map_or_else( || { if *applicability == Applicability::MachineApplicable { *applicability = Applicability::HasPlaceholders; @@ -226,8 +236,12 @@ pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, defau } /// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(cx: &T, span: Span) -> Option { - cx.sess().source_map().span_to_snippet(span).ok() +pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option { + snippet_opt_sess(cx.sess(), span) +} + +fn snippet_opt_sess(sess: &Session, span: Span) -> Option { + sess.source_map().span_to_snippet(span).ok() } /// Converts a span (from a block) to a code snippet if available, otherwise use default. @@ -277,8 +291,8 @@ pub fn snippet_block<'a, T: LintContext>( /// Same as `snippet_block`, but adapts the applicability level by the rules of /// `snippet_with_applicability`. -pub fn snippet_block_with_applicability<'a, T: LintContext>( - cx: &T, +pub fn snippet_block_with_applicability<'a>( + cx: &impl LintContext, span: Span, default: &'a str, indent_relative_to: Option, @@ -299,7 +313,17 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( /// /// This will also return whether or not the snippet is a macro call. pub fn snippet_with_context<'a>( - cx: &LateContext<'_>, + cx: &impl LintContext, + span: Span, + outer: SyntaxContext, + default: &'a str, + applicability: &mut Applicability, +) -> (Cow<'a, str>, bool) { + snippet_with_context_sess(cx.sess(), span, outer, default, applicability) +} + +fn snippet_with_context_sess<'a>( + sess: &Session, span: Span, outer: SyntaxContext, default: &'a str, @@ -318,7 +342,7 @@ pub fn snippet_with_context<'a>( ); ( - snippet_with_applicability(cx, span, default, applicability), + snippet_with_applicability_sess(sess, span, default, applicability), is_macro_call, ) } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3cacdb493772..b66604f33db1 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -176,25 +176,28 @@ impl<'a> Sugg<'a> { } /// Prepare a suggestion from an expression. - pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { + pub fn ast( + cx: &EarlyContext<'_>, + expr: &ast::Expr, + default: &'a str, + ctxt: SyntaxContext, + app: &mut Applicability, + ) -> Self { use rustc_ast::ast::RangeLimits; - let snippet_without_expansion = |cx, span: Span, default| { - if span.from_expansion() { - snippet_with_macro_callsite(cx, span, default) - } else { - snippet(cx, span, default) - } - }; - + #[expect(clippy::match_wildcard_for_single_variants)] match expr.kind { + _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::AddrOf(..) | ast::ExprKind::Box(..) | ast::ExprKind::Closure { .. } | ast::ExprKind::If(..) | ast::ExprKind::Let(..) | ast::ExprKind::Unary(..) - | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)), + | ast::ExprKind::Match(..) => match snippet_with_context(cx, expr.span, ctxt, default, app) { + (snip, false) => Sugg::MaybeParen(snip), + (snip, true) => Sugg::NonParen(snip), + }, ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Break(..) @@ -224,45 +227,49 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Array(..) | ast::ExprKind::While(..) | ast::ExprKind::Await(..) - | ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)), + | ast::ExprKind::Err => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( AssocOp::DotDot, - lhs.as_ref() - .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)), - rhs.as_ref() - .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)), + lhs.as_ref().map_or("".into(), |lhs| { + snippet_with_context(cx, lhs.span, ctxt, default, app).0 + }), + rhs.as_ref().map_or("".into(), |rhs| { + snippet_with_context(cx, rhs.span, ctxt, default, app).0 + }), ), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp( AssocOp::DotDotEq, - lhs.as_ref() - .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)), - rhs.as_ref() - .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)), + lhs.as_ref().map_or("".into(), |lhs| { + snippet_with_context(cx, lhs.span, ctxt, default, app).0 + }), + rhs.as_ref().map_or("".into(), |rhs| { + snippet_with_context(cx, rhs.span, ctxt, default, app).0 + }), ), ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp( AssocOp::Assign, - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, rhs.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, rhs.span, ctxt, default, app).0, ), ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp( astbinop2assignop(op), - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, rhs.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, rhs.span, ctxt, default, app).0, ), ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( AssocOp::from_ast_binop(op.node), - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, rhs.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, rhs.span, ctxt, default, app).0, ), ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp( AssocOp::As, - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, ty.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, ty.span, ctxt, default, app).0, ), ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp( AssocOp::Colon, - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, ty.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, ty.span, ctxt, default, app).0, ), } } diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index 7cd687c95a00..c0e49ff4caa7 100644 --- a/tests/ui/redundant_closure_call_fixable.fixed +++ b/tests/ui/redundant_closure_call_fixable.fixed @@ -25,4 +25,16 @@ fn main() { x * y }; let d = async { something().await }; + + macro_rules! m { + () => { + 0 + }; + } + macro_rules! m2 { + () => { + m!() + }; + } + m2!(); } diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs index 37e4d2238641..9e6e54348a8c 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs @@ -25,4 +25,16 @@ fn main() { x * y })(); let d = (async || something().await)(); + + macro_rules! m { + () => { + (|| 0)() + }; + } + macro_rules! m2 { + () => { + (|| m!())() + }; + } + m2!(); } diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index 56a8e57c0c36..d71bcba2a820 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -52,5 +52,27 @@ error: try not to call a closure in the expression where it is declared LL | let d = (async || something().await)(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }` -error: aborting due to 4 previous errors +error: try not to call a closure in the expression where it is declared + --> $DIR/redundant_closure_call_fixable.rs:36:13 + | +LL | (|| m!())() + | ^^^^^^^^^^^ help: try doing something like: `m!()` +... +LL | m2!(); + | ----- in this macro invocation + | + = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: try not to call a closure in the expression where it is declared + --> $DIR/redundant_closure_call_fixable.rs:31:13 + | +LL | (|| 0)() + | ^^^^^^^^ help: try doing something like: `0` +... +LL | m2!(); + | ----- in this macro invocation + | + = note: this error originates in the macro `m` which comes from the expansion of the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors From 73f4546d24bc00f032d3761fa966f9f5927a8f76 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 30 Nov 2022 10:48:53 -0500 Subject: [PATCH 298/524] Fix `unnecessary_cast` suggestion when taking a reference --- clippy_lints/src/casts/unnecessary_cast.rs | 6 +++++- tests/ui/unnecessary_cast.fixed | 3 +++ tests/ui/unnecessary_cast.rs | 3 +++ tests/ui/unnecessary_cast.stderr | 18 ++++++++++++------ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index ecc8a8de97b9..7e23318076cf 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -90,7 +90,11 @@ pub(super) fn check<'tcx>( expr.span, &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", - cast_str, + if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(..))) { + format!("{{ {cast_str} }}") + } else { + cast_str + }, Applicability::MachineApplicable, ); return true; diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index f234d4473c3e..2f7e2997e739 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -96,6 +96,9 @@ mod fixable { let _ = 1 as I32Alias; let _ = &1 as &I32Alias; + + let x = 1i32; + let _ = &{ x }; } type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 855a4efa0341..54dd46ba59f1 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -96,6 +96,9 @@ mod fixable { let _ = 1 as I32Alias; let _ = &1 as &I32Alias; + + let x = 1i32; + let _ = &(x as i32); } type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index 934db0e86e66..fcee4ee2a65c 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -150,35 +150,41 @@ error: casting float literal to `f32` is unnecessary LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` +error: casting to the same type is unnecessary (`i32` -> `i32`) + --> $DIR/unnecessary_cast.rs:101:18 + | +LL | let _ = &(x as i32); + | ^^^^^^^^^^ help: try: `{ x }` + error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:104:22 + --> $DIR/unnecessary_cast.rs:107:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> $DIR/unnecessary_cast.rs:106:22 + --> $DIR/unnecessary_cast.rs:109:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:113:22 + --> $DIR/unnecessary_cast.rs:116:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:115:23 + --> $DIR/unnecessary_cast.rs:118:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> $DIR/unnecessary_cast.rs:123:20 + --> $DIR/unnecessary_cast.rs:126:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` -error: aborting due to 30 previous errors +error: aborting due to 31 previous errors From 2d32b403596d6ff152ccce3f1136b695698a3cc8 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 30 Nov 2022 11:15:49 -0500 Subject: [PATCH 299/524] Don't lint `explicit_auto_deref` when the initial type is neither a reference, nor a receiver --- clippy_lints/src/dereference.rs | 17 +++++++++++------ tests/ui/explicit_auto_deref.fixed | 4 ++++ tests/ui/explicit_auto_deref.rs | 4 ++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 7de117d55491..57c30661e882 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -289,23 +289,24 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv); match kind { RefOp::Deref => { + let sub_ty = typeck.expr_ty(sub_expr); if let Position::FieldAccess { name, of_union: false, } = position - && !ty_contains_field(typeck.expr_ty(sub_expr), name) + && !ty_contains_field(sub_ty, name) { self.state = Some(( State::ExplicitDerefField { name }, StateData { span: expr.span, hir_id: expr.hir_id, position }, )); - } else if position.is_deref_stable() { + } else if position.is_deref_stable() && sub_ty.is_ref() { self.state = Some(( State::ExplicitDeref { mutability: None }, StateData { span: expr.span, hir_id: expr.hir_id, position }, )); } - } + }, RefOp::Method(target_mut) if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) && position.lint_explicit_deref() => @@ -320,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { StateData { span: expr.span, hir_id: expr.hir_id, - position + position, }, )); }, @@ -394,7 +395,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { msg, snip_expr, }), - StateData { span: expr.span, hir_id: expr.hir_id, position }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + position, + }, )); } else if position.is_deref_stable() // Auto-deref doesn't combine with other adjustments @@ -406,7 +411,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { StateData { span: expr.span, hir_id: expr.hir_id, - position + position, }, )); } diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index 59ff5e4040a3..475fae5e823b 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -277,4 +277,8 @@ fn main() { unimplemented!() } let _: String = takes_assoc(&*String::new()); + + // Issue #9901 + fn takes_ref(_: &i32) {} + takes_ref(*Box::new(&0i32)); } diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index bcfb60c32788..c1894258f4d8 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -277,4 +277,8 @@ fn main() { unimplemented!() } let _: String = takes_assoc(&*String::new()); + + // Issue #9901 + fn takes_ref(_: &i32) {} + takes_ref(*Box::new(&0i32)); } From c1b8bc66e999eda7dfa2887aab2a8b06914ed875 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 30 Nov 2022 12:31:38 -0500 Subject: [PATCH 300/524] Fix ICE in `unused_rounding` --- clippy_lints/src/unused_rounding.rs | 21 ++++++++------------- tests/ui/unused_rounding.fixed | 3 +++ tests/ui/unused_rounding.rs | 3 +++ tests/ui/unused_rounding.stderr | 8 +++++++- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index aac6719a8dc0..097568cd1f70 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -29,22 +30,16 @@ declare_clippy_lint! { } declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); -fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { +fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> { if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") && let ExprKind::Lit(token_lit) = &receiver.kind - && token_lit.is_semantic_float() { - let mut f_str = token_lit.symbol.to_string(); - let f = f_str.trim_end_matches('_').parse::().unwrap(); - if let Some(suffix) = token_lit.suffix { - f_str.push_str(suffix.as_str()); - } - if f.fract() == 0.0 { - Some((method_name, f_str)) - } else { - None - } + && token_lit.is_semantic_float() + && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::() { + (f.fract() == 0.0).then(|| + (method_name, snippet(cx, receiver.span, "..").to_string()) + ) } else { None } @@ -52,7 +47,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { impl EarlyLintPass for UnusedRounding { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if let Some((method_name, float)) = is_useless_rounding(expr) { + if let Some((method_name, float)) = is_useless_rounding(cx, expr) { span_lint_and_sugg( cx, UNUSED_ROUNDING, diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed index 38fe6c34cfec..f6f734c05ed5 100644 --- a/tests/ui/unused_rounding.fixed +++ b/tests/ui/unused_rounding.fixed @@ -11,4 +11,7 @@ fn main() { let _ = 3.3_f32.round(); let _ = 3.3_f64.round(); let _ = 3.0_f32; + + let _ = 3_3.0_0_f32; + let _ = 3_3.0_1_f64.round(); } diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs index a5cac64d023a..a0267d8144aa 100644 --- a/tests/ui/unused_rounding.rs +++ b/tests/ui/unused_rounding.rs @@ -11,4 +11,7 @@ fn main() { let _ = 3.3_f32.round(); let _ = 3.3_f64.round(); let _ = 3.0_f32.round(); + + let _ = 3_3.0_0_f32.round(); + let _ = 3_3.0_1_f64.round(); } diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr index 1eeb5d1de883..b867996fe576 100644 --- a/tests/ui/unused_rounding.stderr +++ b/tests/ui/unused_rounding.stderr @@ -24,5 +24,11 @@ error: used the `round` method with a whole number float LL | let _ = 3.0_f32.round(); | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32` -error: aborting due to 4 previous errors +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:15:13 + | +LL | let _ = 3_3.0_0_f32.round(); + | ^^^^^^^^^^^^^^^^^^^ help: remove the `round` method call: `3_3.0_0_f32` + +error: aborting due to 5 previous errors From 03ba0ea400b42a71455a9774b6cbbf7f28a8e9a7 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 30 Nov 2022 16:25:57 -0500 Subject: [PATCH 301/524] Don't suggest keeping borrows in `identity_op` --- clippy_lints/src/operators/identity_op.rs | 74 ++++++++++++++++++++--- tests/ui/identity_op.fixed | 6 +- tests/ui/identity_op.rs | 4 ++ tests/ui/identity_op.stderr | 12 +++- 4 files changed, 83 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index b48d6c4e2e2a..14a12da862ef 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{clip, unsext}; +use clippy_utils::{clip, peel_hir_expr_refs, unsext}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; use rustc_lint::LateContext; @@ -20,20 +20,76 @@ pub(crate) fn check<'tcx>( if !is_allowed(cx, op, left, right) { match op { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - check_op(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right)); - check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + left, + 0, + expr.span, + peel_hir_expr_refs(right).0.span, + needs_parenthesis(cx, expr, right), + ); + check_op( + cx, + right, + 0, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + right, + 0, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, BinOpKind::Mul => { - check_op(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right)); - check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + left, + 1, + expr.span, + peel_hir_expr_refs(right).0.span, + needs_parenthesis(cx, expr, right), + ); + check_op( + cx, + right, + 1, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, - BinOpKind::Div => check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded), + BinOpKind::Div => check_op( + cx, + right, + 1, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ), BinOpKind::BitAnd => { - check_op(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right)); - check_op(cx, right, -1, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + left, + -1, + expr.span, + peel_hir_expr_refs(right).0.span, + needs_parenthesis(cx, expr, right), + ); + check_op( + cx, + right, + -1, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span), _ => (), diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed index e7b9a78c5dbc..cac69ef42c41 100644 --- a/tests/ui/identity_op.fixed +++ b/tests/ui/identity_op.fixed @@ -65,7 +65,7 @@ fn main() { 42; 1; 42; - &x; + x; x; let mut a = A(String::new()); @@ -112,6 +112,10 @@ fn main() { 2 * { a }; (({ a } + 4)); 1; + + // Issue #9904 + let x = 0i32; + let _: i32 = x; } pub fn decide(a: bool, b: bool) -> u32 { diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 9a435cdbb753..33201aad4f64 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -112,6 +112,10 @@ fn main() { 2 * (0 + { a }); 1 * ({ a } + 4); 1 * 1; + + // Issue #9904 + let x = 0i32; + let _: i32 = &x + 0; } pub fn decide(a: bool, b: bool) -> u32 { diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 1a104a20b841..3ba557d18b24 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -70,7 +70,7 @@ error: this operation has no effect --> $DIR/identity_op.rs:68:5 | LL | &x >> 0; - | ^^^^^^^ help: consider reducing it to: `&x` + | ^^^^^^^ help: consider reducing it to: `x` error: this operation has no effect --> $DIR/identity_op.rs:69:5 @@ -229,10 +229,16 @@ LL | 1 * 1; | ^^^^^ help: consider reducing it to: `1` error: this operation has no effect - --> $DIR/identity_op.rs:118:5 + --> $DIR/identity_op.rs:118:18 + | +LL | let _: i32 = &x + 0; + | ^^^^^^ help: consider reducing it to: `x` + +error: this operation has no effect + --> $DIR/identity_op.rs:122:5 | LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })` -error: aborting due to 39 previous errors +error: aborting due to 40 previous errors From 55096eab0f5b398294cba8b9182e06b1c1e3d423 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 30 Nov 2022 21:29:48 -0500 Subject: [PATCH 302/524] Don't suggest removing `mut` from references in `redundant_static_lifetimes` --- clippy_lints/src/redundant_static_lifetimes.rs | 2 +- tests/ui/redundant_static_lifetimes.fixed | 6 ++++++ tests/ui/redundant_static_lifetimes.rs | 6 ++++++ tests/ui/redundant_static_lifetimes.stderr | 10 ++++++++-- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 3aa2490bc44e..41f991a967bf 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -66,7 +66,7 @@ impl RedundantStaticLifetimes { TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime { let snip = snippet(cx, borrow_type.ty.span, ""); - let sugg = format!("&{snip}"); + let sugg = format!("&{}{snip}", borrow_type.mutbl.prefix_str()); span_lint_and_then( cx, REDUNDANT_STATIC_LIFETIMES, diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index 4c5846fe837e..bca777a890c3 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -39,8 +39,14 @@ static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static. +static mut STATIC_MUT_SLICE: &mut [u32] = &mut [0]; + fn main() { let false_positive: &'static str = "test"; + + unsafe { + STATIC_MUT_SLICE[0] = 0; + } } trait Bar { diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index 64a66be1a83c..afe7644816d2 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -39,8 +39,14 @@ static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. +static mut STATIC_MUT_SLICE: &'static mut [u32] = &mut [0]; + fn main() { let false_positive: &'static str = "test"; + + unsafe { + STATIC_MUT_SLICE[0] = 0; + } } trait Bar { diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 0938ebf783ff..b2cbd2d9d01b 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -97,10 +97,16 @@ LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removin | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:65:16 + --> $DIR/redundant_static_lifetimes.rs:42:31 + | +LL | static mut STATIC_MUT_SLICE: &'static mut [u32] = &mut [0]; + | -^^^^^^^---------- help: consider removing `'static`: `&mut [u32]` + +error: statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:71:16 | LL | static V: &'static u8 = &17; | -^^^^^^^--- help: consider removing `'static`: `&u8` -error: aborting due to 17 previous errors +error: aborting due to 18 previous errors From 7ae5c81e9f9046a547757a1521e83c23da0bb67e Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 30 Nov 2022 21:44:18 -0500 Subject: [PATCH 303/524] Fix ICE in `result large_err` with uninhabited enums --- clippy_lints/src/functions/result.rs | 8 +++++--- tests/ui/result_large_err.rs | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index f7e30b051a69..23da145d0382 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -94,7 +94,9 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty if let hir::ItemKind::Enum(ref def, _) = item.kind; then { let variants_size = AdtVariantInfo::new(cx, *adt, subst); - if variants_size[0].size >= large_err_threshold { + if let Some((first_variant, variants)) = variants_size.split_first() + && first_variant.size >= large_err_threshold + { span_lint_and_then( cx, RESULT_LARGE_ERR, @@ -102,11 +104,11 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty "the `Err`-variant returned from this function is very large", |diag| { diag.span_label( - def.variants[variants_size[0].ind].span, + def.variants[first_variant.ind].span, format!("the largest variant contains at least {} bytes", variants_size[0].size), ); - for variant in &variants_size[1..] { + for variant in variants { if variant.size >= large_err_threshold { let variant_def = &def.variants[variant.ind]; diag.span_label( diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index 9dd27d6dc01a..1c12cebfd971 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -108,4 +108,10 @@ pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { Ok(()) } +// Issue #10005 +enum Empty {} +fn _empty_error() -> Result<(), Empty> { + Ok(()) +} + fn main() {} From a1b15f13f7187b9c61e1d691a339b32550dfed43 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 30 Nov 2022 22:34:42 -0500 Subject: [PATCH 304/524] Treat custom enum discriminant values as constants --- clippy_utils/src/lib.rs | 2 +- tests/ui/cast_lossless_integer.fixed | 6 ++++++ tests/ui/cast_lossless_integer.rs | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 90192f46cbfa..09ed7255a2b2 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -196,7 +196,7 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { let parent_id = cx.tcx.hir().get_parent_item(id).def_id; match cx.tcx.hir().get_by_def_id(parent_id) { Node::Item(&Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), + kind: ItemKind::Const(..) | ItemKind::Static(..) | ItemKind::Enum(..), .. }) | Node::TraitItem(&TraitItem { diff --git a/tests/ui/cast_lossless_integer.fixed b/tests/ui/cast_lossless_integer.fixed index 72a708b40737..925cbf25368f 100644 --- a/tests/ui/cast_lossless_integer.fixed +++ b/tests/ui/cast_lossless_integer.fixed @@ -45,3 +45,9 @@ mod cast_lossless_in_impl { } } } + +#[derive(PartialEq, Debug)] +#[repr(i64)] +enum Test { + A = u32::MAX as i64 + 1, +} diff --git a/tests/ui/cast_lossless_integer.rs b/tests/ui/cast_lossless_integer.rs index 34bb47181e69..c82bd9108d23 100644 --- a/tests/ui/cast_lossless_integer.rs +++ b/tests/ui/cast_lossless_integer.rs @@ -45,3 +45,9 @@ mod cast_lossless_in_impl { } } } + +#[derive(PartialEq, Debug)] +#[repr(i64)] +enum Test { + A = u32::MAX as i64 + 1, +} From 4063712bf45fcb600d23dcaea4540d95311aaa29 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 1 Dec 2022 12:55:29 +0100 Subject: [PATCH 305/524] Bump nightly version -> 2022-12-01 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index a806c1564796..19fee38db46e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-11-21" +channel = "nightly-2022-12-01" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 4f8c49e950d70aad48d2e1ec4642cef0a364c0ec Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 25 Nov 2022 11:26:36 +0300 Subject: [PATCH 306/524] rustc_hir: Relax lifetime requirements on `Visitor::visit_path` --- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/methods/option_map_unwrap_or.rs | 4 ++-- .../src/utils/internal_lints/lint_without_lint_pass.rs | 2 +- clippy_lints/src/utils/internal_lints/metadata_collector.rs | 2 +- clippy_utils/src/usage.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 8b24a4962fb2..6d9ede5f73bb 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -126,7 +126,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> { self.cx.tcx.hir() } - fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) { + fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { for segment in path.segments { match segment.ident.name { kw::SelfLower => self.lower.push(segment.ident.span), diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 30421a6dd5af..910ee14855e2 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -97,7 +97,7 @@ struct UnwrapVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> { type NestedFilter = nested_filter::All; - fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { + fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { self.identifiers.insert(ident(path)); walk_path(self, path); } @@ -116,7 +116,7 @@ struct MapExprVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> { type NestedFilter = nested_filter::All; - fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { + fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { if self.identifiers.contains(&ident(path)) { self.found_identifier = true; return; diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 1aebb8b3104b..786d9608c851 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -330,7 +330,7 @@ struct LintCollector<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { type NestedFilter = nested_filter::All; - fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { + fn visit_path(&mut self, path: &Path<'_>, _: HirId) { if path.segments.len() == 1 { self.output.insert(path.segments[0].ident.name); } diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index d06a616e4b30..857abe77e21f 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -1019,7 +1019,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { self.cx.tcx.hir() } - fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) { + fn visit_path(&mut self, path: &hir::Path<'hir>, _id: hir::HirId) { for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() { if match_path(path, enum_value) { self.add_new_index(index); diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 797722cfc1fc..ab3976a13bdb 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -128,7 +128,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { } } - fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) { + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { if let hir::def::Res::Local(id) = path.res { if self.binding_ids.contains(&id) { self.usage_found = true; From 9314e5b942c1e2cbc9d4c7d520ca7102675d6d08 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 25 Nov 2022 17:39:38 +0300 Subject: [PATCH 307/524] rustc_hir: Change representation of import paths to support multiple resolutions --- clippy_lints/src/disallowed_types.rs | 4 +- clippy_lints/src/macro_use.rs | 5 +- .../src/missing_enforced_import_rename.rs | 59 ++++++++++--------- clippy_lints/src/redundant_pub_crate.rs | 2 +- clippy_lints/src/wildcard_imports.rs | 3 +- 5 files changed, 41 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index aee3d8c4f085..1f56d0118a40 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -106,7 +106,9 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Use(path, UseKind::Single) = &item.kind { - self.check_res_emit(cx, &path.res, item.span); + for res in &path.res { + self.check_res_emit(cx, res, item.span); + } } } diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 594f6af76b3d..e2e6a87a3015 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -94,7 +94,10 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { let hir_id = item.hir_id(); let attrs = cx.tcx.hir().attrs(hir_id); if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)); - if let Res::Def(DefKind::Mod, id) = path.res; + if let Some(id) = path.res.iter().find_map(|res| match res { + Res::Def(DefKind::Mod, id) => Some(id), + _ => None, + }); if !id.is_local(); then { for kid in cx.tcx.module_children(id).iter() { diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 4712846939e6..773174679dbd 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -66,35 +66,38 @@ impl LateLintPass<'_> for ImportRename { } fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if_chain! { - if let ItemKind::Use(path, UseKind::Single) = &item.kind; - if let Res::Def(_, id) = path.res; - if let Some(name) = self.renames.get(&id); - // Remove semicolon since it is not present for nested imports - let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';'); - if let Some(snip) = snippet_opt(cx, span_without_semi); - if let Some(import) = match snip.split_once(" as ") { - None => Some(snip.as_str()), - Some((import, rename)) => { - if rename.trim() == name.as_str() { - None - } else { - Some(import.trim()) + if let ItemKind::Use(path, UseKind::Single) = &item.kind { + for &res in &path.res { + if_chain! { + if let Res::Def(_, id) = res; + if let Some(name) = self.renames.get(&id); + // Remove semicolon since it is not present for nested imports + let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';'); + if let Some(snip) = snippet_opt(cx, span_without_semi); + if let Some(import) = match snip.split_once(" as ") { + None => Some(snip.as_str()), + Some((import, rename)) => { + if rename.trim() == name.as_str() { + None + } else { + Some(import.trim()) + } + }, + }; + then { + span_lint_and_sugg( + cx, + MISSING_ENFORCED_IMPORT_RENAMES, + span_without_semi, + "this import should be renamed", + "try", + format!( + "{import} as {name}", + ), + Applicability::MachineApplicable, + ); } - }, - }; - then { - span_lint_and_sugg( - cx, - MISSING_ENFORCED_IMPORT_RENAMES, - span_without_semi, - "this import should be renamed", - "try", - format!( - "{import} as {name}", - ), - Applicability::MachineApplicable, - ); + } } } } diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 833dc4913b46..d612d249c2f0 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { if let ItemKind::Use(path, _) = item.kind { - if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res { + if path.res.iter().all(|res| matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _))) { return false; } } else if let ItemKind::Macro(..) = item.kind { diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index be98344470b9..e4d1ee195c4d 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -176,7 +176,8 @@ impl LateLintPass<'_> for WildcardImports { format!("{import_source_snippet}::{imports_string}") }; - let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res { + // Glob imports always have a single resolution. + let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] { (ENUM_GLOB_USE, "usage of wildcard import for enum variants") } else { (WILDCARD_IMPORTS, "usage of wildcard import") From b0d490e308561eaa3b30067fe6e35c47d47e8896 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 1 Dec 2022 18:51:20 +0300 Subject: [PATCH 308/524] rustc_ast_lowering: Stop lowering imports into multiple items Lower them into a single item with multiple resolutions instead. This also allows to remove additional `NodId`s and `DefId`s related to those additional items. --- clippy_lints/src/single_component_path_imports.rs | 4 ++-- clippy_lints/src/unnecessary_self_imports.rs | 2 +- clippy_lints/src/unsafe_removed_from_name.rs | 4 ++-- clippy_utils/src/ast_utils.rs | 2 +- tests/ui/macro_use_imports.stderr | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 2036e85db7e8..d46f6a6352c6 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -149,7 +149,7 @@ impl SingleComponentPathImports { // keep track of `use some_module;` usages if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + if let UseTreeKind::Simple(None) = use_tree.kind { let name = segments[0].ident.name; if !macros.contains(&name) { single_use_usages.push(SingleUse { @@ -169,7 +169,7 @@ impl SingleComponentPathImports { for tree in trees { let segments = &tree.0.prefix.segments; if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = tree.0.kind { + if let UseTreeKind::Simple(None) = tree.0.kind { let name = segments[0].ident.name; if !macros.contains(&name) { single_use_usages.push(SingleUse { diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs index bc0dd263d88a..397633f533b2 100644 --- a/clippy_lints/src/unnecessary_self_imports.rs +++ b/clippy_lints/src/unnecessary_self_imports.rs @@ -57,7 +57,7 @@ impl EarlyLintPass for UnnecessarySelfImports { format!( "{}{};", last_segment.ident, - if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {alias}") } else { String::new() }, + if let UseTreeKind::Simple(Some(alias)) = self_tree.kind { format!(" as {alias}") } else { String::new() }, ), Applicability::MaybeIncorrect, ); diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 952586527689..7ee785804f0a 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -39,7 +39,7 @@ impl EarlyLintPass for UnsafeNameRemoval { fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { match use_tree.kind { - UseTreeKind::Simple(Some(new_name), ..) => { + UseTreeKind::Simple(Some(new_name)) => { let old_name = use_tree .prefix .segments @@ -48,7 +48,7 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { .ident; unsafe_to_safe_check(old_name, new_name, cx, span); }, - UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {}, + UseTreeKind::Simple(None) | UseTreeKind::Glob => {}, UseTreeKind::Nested(ref nested_use_tree) => { for (use_tree, _) in nested_use_tree { check_use_tree(use_tree, cx, span); diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 6bcf0bbd7eb7..49e5f283db08 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -566,7 +566,7 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { use UseTreeKind::*; match (l, r) { (Glob, Glob) => true, - (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)), + (Simple(l), Simple(r)) => both(l, r, |l, r| eq_id(*l, *r)), (Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)), _ => false, } diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr index bf7b6edd0e31..61843124ccd9 100644 --- a/tests/ui/macro_use_imports.stderr +++ b/tests/ui/macro_use_imports.stderr @@ -1,8 +1,8 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:23:5 + --> $DIR/macro_use_imports.rs:25:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` | = note: `-D clippy::macro-use-imports` implied by `-D warnings` @@ -13,10 +13,10 @@ LL | #[macro_use] | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:25:5 + --> $DIR/macro_use_imports.rs:23:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:19:5 From 6ecdff07e5c38f3cf53f299219ba575ebd4f9831 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 29 Nov 2022 00:20:53 -0500 Subject: [PATCH 309/524] Don't lint `from_over_into` for opaque types --- clippy_lints/src/from_over_into.rs | 3 ++- tests/ui/from_over_into.fixed | 7 +++++++ tests/ui/from_over_into.rs | 7 +++++++ tests/ui/from_over_into.stderr | 10 +++++----- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 8621504c1b47..cc3c35f48635 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -10,7 +10,7 @@ use rustc_hir::{ TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::nested_filter::OnlyBodies; +use rustc_middle::{hir::nested_filter::OnlyBodies, ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, sym}; use rustc_span::{Span, Symbol}; @@ -78,6 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) + && !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Opaque(..)) { span_lint_and_then( cx, diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed index 125c9a69cd3f..72d635c2ccd6 100644 --- a/tests/ui/from_over_into.fixed +++ b/tests/ui/from_over_into.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(type_alias_impl_trait)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -81,4 +82,10 @@ fn msrv_1_41() { } } +type Opaque = impl Sized; +struct IntoOpaque; +impl Into for IntoOpaque { + fn into(self) -> Opaque {} +} + fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 5aa127bfabe4..965f4d5d7859 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(type_alias_impl_trait)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -81,4 +82,10 @@ fn msrv_1_41() { } } +type Opaque = impl Sized; +struct IntoOpaque; +impl Into for IntoOpaque { + fn into(self) -> Opaque {} +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index a1764a5ea12a..3c4d011d6fb4 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,5 +1,5 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:9:1 + --> $DIR/from_over_into.rs:10:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL ~ StringWrapper(val) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:17:1 + --> $DIR/from_over_into.rs:18:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL ~ SelfType(String::new()) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:32:1 + --> $DIR/from_over_into.rs:33:1 | LL | impl Into for X { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL ~ let _: X = val; | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:44:1 + --> $DIR/from_over_into.rs:45:1 | LL | impl core::convert::Into for crate::ExplicitPaths { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ LL ~ val.0 | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:77:5 + --> $DIR/from_over_into.rs:78:5 | LL | impl Into> for Vec { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From d05e2865a05e86de9cfd283d4a6f88340346f48b Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 1 Dec 2022 18:29:38 +0100 Subject: [PATCH 310/524] Merge commit 'd822110d3b5625b9dc80ccc442e06fc3cc851d76' into clippyup --- CHANGELOG.md | 2 + README.md | 6 +- book/src/SUMMARY.md | 1 + book/src/configuration.md | 6 +- book/src/development/adding_lints.md | 18 +- .../proposals/syntax-tree-patterns.md | 986 ++++++++++++++++++ clippy_dev/src/new_lint.rs | 16 +- .../src/almost_complete_letter_range.rs | 11 +- clippy_lints/src/approx_const.rs | 8 +- clippy_lints/src/attrs.rs | 12 +- .../src/casts/cast_abs_to_unsigned.rs | 7 +- clippy_lints/src/casts/cast_lossless.rs | 16 +- .../src/casts/cast_possible_truncation.rs | 7 +- .../src/casts/cast_slice_different_sizes.rs | 8 +- .../src/casts/cast_slice_from_raw_parts.rs | 14 +- clippy_lints/src/casts/mod.rs | 22 +- clippy_lints/src/casts/ptr_as_ptr.rs | 7 +- clippy_lints/src/casts/unnecessary_cast.rs | 17 +- clippy_lints/src/checked_conversions.rs | 10 +- clippy_lints/src/collapsible_if.rs | 10 +- clippy_lints/src/declared_lints.rs | 2 + clippy_lints/src/dereference.rs | 41 +- clippy_lints/src/derive.rs | 4 +- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/eta_reduction.rs | 22 +- clippy_lints/src/exit.rs | 23 +- clippy_lints/src/format_args.rs | 82 +- clippy_lints/src/from_over_into.rs | 10 +- .../src/functions/misnamed_getters.rs | 125 +++ clippy_lints/src/functions/mod.rs | 45 + clippy_lints/src/functions/result.rs | 8 +- clippy_lints/src/future_not_send.rs | 4 +- clippy_lints/src/if_then_some_else_none.rs | 14 +- clippy_lints/src/index_refutable_slice.rs | 13 +- clippy_lints/src/instant_subtraction.rs | 18 +- clippy_lints/src/lib.rs | 109 +- clippy_lints/src/lifetimes.rs | 13 +- clippy_lints/src/manual_bits.rs | 10 +- clippy_lints/src/manual_clamp.rs | 33 +- clippy_lints/src/manual_is_ascii_check.rs | 15 +- clippy_lints/src/manual_let_else.rs | 36 +- clippy_lints/src/manual_non_exhaustive.rs | 16 +- clippy_lints/src/manual_rem_euclid.rs | 12 +- clippy_lints/src/manual_retain.rs | 24 +- clippy_lints/src/manual_strip.rs | 10 +- clippy_lints/src/matches/mod.rs | 14 +- clippy_lints/src/matches/try_err.rs | 6 +- clippy_lints/src/mem_replace.rs | 10 +- .../src/methods/cloned_instead_of_copied.rs | 10 +- clippy_lints/src/methods/err_expect.rs | 8 +- clippy_lints/src/methods/filter_map_next.rs | 8 +- .../src/methods/is_digit_ascii_radix.rs | 9 +- clippy_lints/src/methods/map_clone.rs | 16 +- clippy_lints/src/methods/map_unwrap_or.rs | 7 +- clippy_lints/src/methods/mod.rs | 36 +- .../src/methods/option_as_ref_deref.rs | 8 +- clippy_lints/src/methods/str_splitn.rs | 8 +- .../src/methods/unnecessary_to_owned.rs | 61 +- clippy_lints/src/missing_const_for_fn.rs | 14 +- clippy_lints/src/needless_pass_by_value.rs | 8 +- clippy_lints/src/no_effect.rs | 8 +- clippy_lints/src/ptr.rs | 9 +- clippy_lints/src/ranges.rs | 10 +- clippy_lints/src/redundant_closure_call.rs | 4 +- clippy_lints/src/redundant_field_names.rs | 9 +- .../src/redundant_static_lifetimes.rs | 9 +- clippy_lints/src/returns.rs | 29 +- clippy_lints/src/transmute/mod.rs | 8 +- .../src/transmute/transmute_ptr_to_ref.rs | 10 +- .../src/undocumented_unsafe_blocks.rs | 429 ++++++-- clippy_lints/src/unnested_or_patterns.rs | 17 +- clippy_lints/src/unused_rounding.rs | 21 +- clippy_lints/src/use_self.rs | 14 +- clippy_lints/src/utils/conf.rs | 4 + .../utils/internal_lints/msrv_attr_impl.rs | 2 +- clippy_utils/src/attrs.rs | 24 +- clippy_utils/src/eager_or_lazy.rs | 26 +- clippy_utils/src/lib.rs | 33 +- clippy_utils/src/msrvs.rs | 101 ++ clippy_utils/src/paths.rs | 5 +- clippy_utils/src/qualify_min_const_fn.rs | 28 +- clippy_utils/src/source.rs | 38 +- clippy_utils/src/sugg.rs | 65 +- clippy_utils/src/ty.rs | 38 +- clippy_utils/src/visitors.rs | 12 +- lintcheck/src/main.rs | 15 +- rust-toolchain | 2 +- src/driver.rs | 5 + .../ui-internal/invalid_msrv_attr_impl.fixed | 4 +- tests/ui-internal/invalid_msrv_attr_impl.rs | 4 +- ...unnecessary_def_path_hardcoded_path.stderr | 18 +- .../clippy.toml | 1 + .../uninlined_format_args.fixed | 14 + .../uninlined_format_args.rs | 14 + .../uninlined_format_args.stderr | 76 ++ .../toml_unknown_key/conf_unknown_key.stderr | 1 + tests/ui/almost_complete_letter_range.fixed | 5 +- tests/ui/almost_complete_letter_range.rs | 5 +- tests/ui/almost_complete_letter_range.stderr | 26 +- tests/ui/cast_abs_to_unsigned.fixed | 7 +- tests/ui/cast_abs_to_unsigned.rs | 7 +- tests/ui/cast_abs_to_unsigned.stderr | 36 +- tests/ui/cast_lossless_bool.fixed | 7 +- tests/ui/cast_lossless_bool.rs | 7 +- tests/ui/cast_lossless_bool.stderr | 28 +- tests/ui/cfg_attr_rustfmt.fixed | 8 +- tests/ui/cfg_attr_rustfmt.rs | 8 +- tests/ui/cfg_attr_rustfmt.stderr | 2 +- tests/ui/checked_conversions.fixed | 7 +- tests/ui/checked_conversions.rs | 7 +- tests/ui/checked_conversions.stderr | 34 +- tests/ui/cloned_instead_of_copied.fixed | 10 +- tests/ui/cloned_instead_of_copied.rs | 10 +- tests/ui/cloned_instead_of_copied.stderr | 16 +- tests/ui/err_expect.fixed | 7 +- tests/ui/err_expect.rs | 7 +- tests/ui/err_expect.stderr | 4 +- tests/ui/eta.fixed | 22 + tests/ui/eta.rs | 22 + tests/ui/eta.stderr | 26 +- tests/ui/explicit_auto_deref.fixed | 4 + tests/ui/explicit_auto_deref.rs | 4 + tests/ui/filter_map_next_fixable.fixed | 7 +- tests/ui/filter_map_next_fixable.rs | 7 +- tests/ui/filter_map_next_fixable.stderr | 4 +- tests/ui/from_over_into.fixed | 7 +- tests/ui/from_over_into.rs | 7 +- tests/ui/from_over_into.stderr | 10 +- tests/ui/if_then_some_else_none.rs | 5 +- tests/ui/if_then_some_else_none.stderr | 10 +- tests/ui/manual_clamp.rs | 7 +- tests/ui/manual_clamp.stderr | 70 +- tests/ui/manual_is_ascii_check.fixed | 11 +- tests/ui/manual_is_ascii_check.rs | 11 +- tests/ui/manual_is_ascii_check.stderr | 22 +- tests/ui/manual_let_else.rs | 14 + tests/ui/manual_let_else.stderr | 11 +- tests/ui/manual_rem_euclid.fixed | 13 +- tests/ui/manual_rem_euclid.rs | 13 +- tests/ui/manual_rem_euclid.stderr | 20 +- tests/ui/manual_retain.fixed | 7 +- tests/ui/manual_retain.rs | 7 +- tests/ui/manual_retain.stderr | 38 +- tests/ui/manual_split_once.fixed | 5 +- tests/ui/manual_split_once.rs | 5 +- tests/ui/manual_split_once.stderr | 38 +- tests/ui/manual_str_repeat.fixed | 5 +- tests/ui/manual_str_repeat.rs | 5 +- tests/ui/manual_str_repeat.stderr | 20 +- tests/ui/manual_strip.rs | 7 +- tests/ui/manual_strip.stderr | 32 +- tests/ui/map_unwrap_or.rs | 7 +- tests/ui/map_unwrap_or.stderr | 24 +- tests/ui/match_expr_like_matches_macro.fixed | 7 +- tests/ui/match_expr_like_matches_macro.rs | 7 +- tests/ui/match_expr_like_matches_macro.stderr | 28 +- tests/ui/mem_replace.fixed | 7 +- tests/ui/mem_replace.rs | 7 +- tests/ui/mem_replace.stderr | 40 +- tests/ui/min_rust_version_attr.rs | 41 +- tests/ui/min_rust_version_attr.stderr | 26 +- tests/ui/min_rust_version_invalid_attr.stderr | 2 +- tests/ui/misnamed_getters.rs | 124 +++ tests/ui/misnamed_getters.stderr | 166 +++ .../ui/missing_const_for_fn/cant_be_const.rs | 4 +- .../ui/missing_const_for_fn/could_be_const.rs | 10 +- .../could_be_const.stderr | 24 +- tests/ui/needless_borrow.fixed | 33 +- tests/ui/needless_borrow.rs | 33 +- tests/ui/needless_borrow.stderr | 18 +- tests/ui/needless_question_mark.fixed | 1 - tests/ui/needless_question_mark.rs | 1 - tests/ui/needless_question_mark.stderr | 28 +- tests/ui/needless_return.fixed | 19 +- tests/ui/needless_return.rs | 13 + tests/ui/needless_return.stderr | 85 +- tests/ui/needless_splitn.fixed | 3 +- tests/ui/needless_splitn.rs | 3 +- tests/ui/needless_splitn.stderr | 26 +- tests/ui/option_as_ref_deref.fixed | 7 +- tests/ui/option_as_ref_deref.rs | 7 +- tests/ui/option_as_ref_deref.stderr | 36 +- tests/ui/ptr_as_ptr.fixed | 5 +- tests/ui/ptr_as_ptr.rs | 5 +- tests/ui/ptr_as_ptr.stderr | 16 +- tests/ui/range_contains.fixed | 7 +- tests/ui/range_contains.rs | 7 +- tests/ui/range_contains.stderr | 42 +- tests/ui/redundant_closure_call_fixable.fixed | 12 + tests/ui/redundant_closure_call_fixable.rs | 12 + .../ui/redundant_closure_call_fixable.stderr | 24 +- tests/ui/redundant_field_names.fixed | 7 +- tests/ui/redundant_field_names.rs | 7 +- tests/ui/redundant_field_names.stderr | 16 +- tests/ui/redundant_static_lifetimes.fixed | 7 +- tests/ui/redundant_static_lifetimes.rs | 7 +- tests/ui/redundant_static_lifetimes.stderr | 34 +- tests/ui/result_large_err.rs | 6 + tests/ui/seek_from_current.fixed | 5 +- tests/ui/seek_from_current.rs | 5 +- tests/ui/seek_from_current.stderr | 2 +- .../ui/seek_to_start_instead_of_rewind.fixed | 7 +- tests/ui/seek_to_start_instead_of_rewind.rs | 7 +- .../ui/seek_to_start_instead_of_rewind.stderr | 6 +- tests/ui/transmute_ptr_to_ref.fixed | 5 +- tests/ui/transmute_ptr_to_ref.rs | 5 +- tests/ui/transmute_ptr_to_ref.stderr | 44 +- tests/ui/undocumented_unsafe_blocks.rs | 2 +- tests/ui/undocumented_unsafe_blocks.stderr | 33 +- tests/ui/uninlined_format_args.fixed | 24 +- tests/ui/uninlined_format_args.rs | 5 +- tests/ui/uninlined_format_args.stderr | 213 ++-- tests/ui/unnecessary_cast.fixed | 14 + tests/ui/unnecessary_cast.rs | 14 + tests/ui/unnecessary_cast.stderr | 52 +- tests/ui/unnecessary_lazy_eval.fixed | 23 +- tests/ui/unnecessary_lazy_eval.rs | 23 +- tests/ui/unnecessary_lazy_eval.stderr | 84 +- tests/ui/unnecessary_operation.fixed | 9 + tests/ui/unnecessary_operation.rs | 9 + tests/ui/unnecessary_safety_comment.rs | 51 + tests/ui/unnecessary_safety_comment.stderr | 115 ++ tests/ui/unnecessary_to_owned.fixed | 34 +- tests/ui/unnecessary_to_owned.rs | 34 +- tests/ui/unnecessary_to_owned.stderr | 168 +-- ..._unsafe.rs => unnecessary_unsafety_doc.rs} | 1 + ...stderr => unnecessary_unsafety_doc.stderr} | 14 +- tests/ui/unnested_or_patterns.fixed | 8 +- tests/ui/unnested_or_patterns.rs | 8 +- tests/ui/unnested_or_patterns.stderr | 2 +- tests/ui/unused_rounding.fixed | 3 + tests/ui/unused_rounding.rs | 3 + tests/ui/unused_rounding.stderr | 8 +- tests/ui/use_self.fixed | 7 +- tests/ui/use_self.rs | 7 +- tests/ui/use_self.stderr | 84 +- triagebot.toml | 21 +- 237 files changed, 4245 insertions(+), 1914 deletions(-) create mode 100644 book/src/development/proposals/syntax-tree-patterns.md create mode 100644 clippy_lints/src/functions/misnamed_getters.rs create mode 100644 tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml create mode 100644 tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed create mode 100644 tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs create mode 100644 tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr create mode 100644 tests/ui/misnamed_getters.rs create mode 100644 tests/ui/misnamed_getters.stderr create mode 100644 tests/ui/unnecessary_safety_comment.rs create mode 100644 tests/ui/unnecessary_safety_comment.stderr rename tests/ui/{doc_unnecessary_unsafe.rs => unnecessary_unsafety_doc.rs} (98%) rename tests/ui/{doc_unnecessary_unsafe.stderr => unnecessary_unsafety_doc.stderr} (81%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f1f73c1fd2f..23912bb3ed6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4188,6 +4188,7 @@ Released 2018-09-13 [`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute [`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os [`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order +[`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items @@ -4450,6 +4451,7 @@ Released 2018-09-13 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings +[`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment [`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by diff --git a/README.md b/README.md index f74de7de42b8..81254ba8b8b8 100644 --- a/README.md +++ b/README.md @@ -197,8 +197,8 @@ disallowed-names = ["toto", "tata", "titi"] cognitive-complexity-threshold = 30 ``` -See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which -lints can be configured and the meaning of the variables. +See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), +the lint descriptions contain the names and meanings of these configuration variables. > **Note** > @@ -224,7 +224,7 @@ in the `Cargo.toml` can be used. rust-version = "1.30" ``` -The MSRV can also be specified as an inner attribute, like below. +The MSRV can also be specified as an attribute, like below. ```rust #![feature(custom_inner_attributes)] diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 0b945faf9b78..1f0b8db28a15 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -21,3 +21,4 @@ - [The Clippy Book](development/infrastructure/book.md) - [Proposals](development/proposals/README.md) - [Roadmap 2021](development/proposals/roadmap-2021.md) + - [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md) diff --git a/book/src/configuration.md b/book/src/configuration.md index 77f1d2e8797a..430ff8b739ae 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -11,8 +11,8 @@ disallowed-names = ["toto", "tata", "titi"] cognitive-complexity-threshold = 30 ``` -See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which -lints can be configured and the meaning of the variables. +See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), +the lint descriptions contain the names and meanings of these configuration variables. To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. @@ -72,7 +72,7 @@ minimum supported Rust version (MSRV) in the clippy configuration file. msrv = "1.30.0" ``` -The MSRV can also be specified as an inner attribute, like below. +The MSRV can also be specified as an attribute, like below. ```rust #![feature(custom_inner_attributes)] diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 3c3f368a529b..8b4eee8c9d94 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -443,27 +443,27 @@ value is passed to the constructor in `clippy_lints/lib.rs`. ```rust pub struct ManualStrip { - msrv: Option, + msrv: Msrv, } impl ManualStrip { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } ``` The project's MSRV can then be matched against the feature MSRV in the LintPass -using the `meets_msrv` utility function. +using the `Msrv::meets` method. ``` rust -if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { +if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) { return; } ``` -The project's MSRV can also be specified as an inner attribute, which overrides +The project's MSRV can also be specified as an attribute, which overrides the value from `clippy.toml`. This can be accounted for using the `extract_msrv_attr!(LintContext)` macro and passing `LateContext`/`EarlyContext`. @@ -483,19 +483,15 @@ have a case for the version below the MSRV and one with the same contents but for the MSRV version itself. ```rust -#![feature(custom_inner_attributes)] - ... +#[clippy::msrv = "1.44"] fn msrv_1_44() { - #![clippy::msrv = "1.44"] - /* something that would trigger the lint */ } +#[clippy::msrv = "1.45"] fn msrv_1_45() { - #![clippy::msrv = "1.45"] - /* something that would trigger the lint */ } ``` diff --git a/book/src/development/proposals/syntax-tree-patterns.md b/book/src/development/proposals/syntax-tree-patterns.md new file mode 100644 index 000000000000..c5587c4bf908 --- /dev/null +++ b/book/src/development/proposals/syntax-tree-patterns.md @@ -0,0 +1,986 @@ +- Feature Name: syntax-tree-patterns +- Start Date: 2019-03-12 +- RFC PR: [#3875](https://github.com/rust-lang/rust-clippy/pull/3875) + +# Summary + +Introduce a domain-specific language (similar to regular expressions) that +allows to describe lints using *syntax tree patterns*. + + +# Motivation + +Finding parts of a syntax tree (AST, HIR, ...) that have certain properties +(e.g. "*an if that has a block as its condition*") is a major task when writing +lints. For non-trivial lints, it often requires nested pattern matching of AST / +HIR nodes. For example, testing that an expression is a boolean literal requires +the following checks: + +```rust +if let ast::ExprKind::Lit(lit) = &expr.node { + if let ast::LitKind::Bool(_) = &lit.node { + ... + } +} +``` + +Writing this kind of matching code quickly becomes a complex task and the +resulting code is often hard to comprehend. The code below shows a simplified +version of the pattern matching required by the `collapsible_if` lint: + +```rust +// simplified version of the collapsible_if lint +if let ast::ExprKind::If(check, then, None) = &expr.node { + if then.stmts.len() == 1 { + if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node { + if let ast::ExprKind::If(check_inner, content, None) = &inner.node { + ... + } + } + } +} +``` + +The `if_chain` macro can improve readability by flattening the nested if +statements, but the resulting code is still quite hard to read: + +```rust +// simplified version of the collapsible_if lint +if_chain! { + if let ast::ExprKind::If(check, then, None) = &expr.node; + if then.stmts.len() == 1; + if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node; + if let ast::ExprKind::If(check_inner, content, None) = &inner.node; + then { + ... + } +} +``` + +The code above matches if expressions that contain only another if expression +(where both ifs don't have an else branch). While it's easy to explain what the +lint does, it's hard to see that from looking at the code samples above. + +Following the motivation above, the first goal this RFC is to **simplify writing +and reading lints**. + +The second part of the motivation is clippy's dependence on unstable +compiler-internal data structures. Clippy lints are currently written against +the compiler's AST / HIR which means that even small changes in these data +structures might break a lot of lints. The second goal of this RFC is to **make +lints independant of the compiler's AST / HIR data structures**. + +# Approach + +A lot of complexity in writing lints currently seems to come from having to +manually implement the matching logic (see code samples above). It's an +imparative style that describes *how* to match a syntax tree node instead of +specifying *what* should be matched against declaratively. In other areas, it's +common to use declarative patterns to describe desired information and let the +implementation do the actual matching. A well-known example of this approach are +[regular expressions](https://en.wikipedia.org/wiki/Regular_expression). Instead +of writing code that detects certain character sequences, one can describe a +search pattern using a domain-specific language and search for matches using +that pattern. The advantage of using a declarative domain-specific language is +that its limited domain (e.g. matching character sequences in the case of +regular expressions) allows to express entities in that domain in a very natural +and expressive way. + +While regular expressions are very useful when searching for patterns in flat +character sequences, they cannot easily be applied to hierarchical data +structures like syntax trees. This RFC therefore proposes a pattern matching +system that is inspired by regular expressions and designed for hierarchical +syntax trees. + +# Guide-level explanation + +This proposal adds a `pattern!` macro that can be used to specify a syntax tree +pattern to search for. A simple pattern is shown below: + +```rust +pattern!{ + my_pattern: Expr = + Lit(Bool(false)) +} +``` + +This macro call defines a pattern named `my_pattern` that can be matched against +an `Expr` syntax tree node. The actual pattern (`Lit(Bool(false))` in this case) +defines which syntax trees should match the pattern. This pattern matches +expressions that are boolean literals with value `false`. + +The pattern can then be used to implement lints in the following way: + +```rust +... + +impl EarlyLintPass for MyAwesomeLint { + fn check_expr(&mut self, cx: &EarlyContext, expr: &syntax::ast::Expr) { + + if my_pattern(expr).is_some() { + cx.span_lint( + MY_AWESOME_LINT, + expr.span, + "This is a match for a simple pattern. Well done!", + ); + } + + } +} +``` + +The `pattern!` macro call expands to a function `my_pattern` that expects a +syntax tree expression as its argument and returns an `Option` that indicates +whether the pattern matched. + +> Note: The result type is explained in more detail in [a later +> section](#the-result-type). For now, it's enough to know that the result is +> `Some` if the pattern matched and `None` otherwise. + +## Pattern syntax + +The following examples demonstate the pattern syntax: + + +#### Any (`_`) + +The simplest pattern is the any pattern. It matches anything and is therefore +similar to regex's `*`. + +```rust +pattern!{ + // matches any expression + my_pattern: Expr = + _ +} +``` + +#### Node (`()`) + +Nodes are used to match a specific variant of an AST node. A node has a name and +a number of arguments that depends on the node type. For example, the `Lit` node +has a single argument that describes the type of the literal. As another +example, the `If` node has three arguments describing the if's condition, then +block and else block. + +```rust +pattern!{ + // matches any expression that is a literal + my_pattern: Expr = + Lit(_) +} + +pattern!{ + // matches any expression that is a boolean literal + my_pattern: Expr = + Lit(Bool(_)) +} + +pattern!{ + // matches if expressions that have a boolean literal in their condition + // Note: The `_?` syntax here means that the else branch is optional and can be anything. + // This is discussed in more detail in the section `Repetition`. + my_pattern: Expr = + If( Lit(Bool(_)) , _, _?) +} +``` + + +#### Literal (``) + +A pattern can also contain Rust literals. These literals match themselves. + +```rust +pattern!{ + // matches the boolean literal false + my_pattern: Expr = + Lit(Bool(false)) +} + +pattern!{ + // matches the character literal 'x' + my_pattern: Expr = + Lit(Char('x')) +} +``` + +#### Alternations (`a | b`) + +```rust +pattern!{ + // matches if the literal is a boolean or integer literal + my_pattern: Lit = + Bool(_) | Int(_) +} + +pattern!{ + // matches if the expression is a char literal with value 'x' or 'y' + my_pattern: Expr = + Lit( Char('x' | 'y') ) +} +``` + +#### Empty (`()`) + +The empty pattern represents an empty sequence or the `None` variant of an +optional. + +```rust +pattern!{ + // matches if the expression is an empty array + my_pattern: Expr = + Array( () ) +} + +pattern!{ + // matches if expressions that don't have an else clause + my_pattern: Expr = + If(_, _, ()) +} +``` + +#### Sequence (` `) + +```rust +pattern!{ + // matches the array [true, false] + my_pattern: Expr = + Array( Lit(Bool(true)) Lit(Bool(false)) ) +} +``` + +#### Repetition (`*`, `+`, `?`, `{n}`, `{n,m}`, `{n,}`) + +Elements may be repeated. The syntax for specifying repetitions is identical to +[regex's syntax](https://docs.rs/regex/1.1.2/regex/#repetitions). + +```rust +pattern!{ + // matches arrays that contain 2 'x's as their last or second-last elements + // Examples: + // ['x', 'x'] match + // ['x', 'x', 'y'] match + // ['a', 'b', 'c', 'x', 'x', 'y'] match + // ['x', 'x', 'y', 'z'] no match + my_pattern: Expr = + Array( _* Lit(Char('x')){2} _? ) +} + +pattern!{ + // matches if expressions that **may or may not** have an else block + // Attn: `If(_, _, _)` matches only ifs that **have** an else block + // + // | if with else block | if witout else block + // If(_, _, _) | match | no match + // If(_, _, _?) | match | match + // If(_, _, ()) | no match | match + my_pattern: Expr = + If(_, _, _?) +} +``` + +#### Named submatch (`#`) + +```rust +pattern!{ + // matches character literals and gives the literal the name foo + my_pattern: Expr = + Lit(Char(_)#foo) +} + +pattern!{ + // matches character literals and gives the char the name bar + my_pattern: Expr = + Lit(Char(_#bar)) +} + +pattern!{ + // matches character literals and gives the expression the name baz + my_pattern: Expr = + Lit(Char(_))#baz +} +``` + +The reason for using named submatches is described in the section [The result +type](#the-result-type). + +### Summary + +The following table gives an summary of the pattern syntax: + +| Syntax | Concept | Examples | +|-------------------------|------------------|--------------------------------------------| +|`_` | Any | `_` | +|`()` | Node | `Lit(Bool(true))`, `If(_, _, _)` | +|`` | Literal | `'x'`, `false`, `101` | +|` \| ` | Alternation | `Char(_) \| Bool(_)` | +|`()` | Empty | `Array( () )` | +|` ` | Sequence | `Tuple( Lit(Bool(_)) Lit(Int(_)) Lit(_) )` | +|`*`
    `
    +`
    `
    ?`
    `
    {n}`
    `
    {n,m}`
    `
    {n,}` | Repetition





    | `Array( _* )`,
    `Block( Semi(_)+ )`,
    `If(_, _, Block(_)?)`,
    `Array( Lit(_){10} )`,
    `Lit(_){5,10}`,
    `Lit(Bool(_)){10,}` | +|`
    #` | Named submatch | `Lit(Int(_))#foo` `Lit(Int(_#bar))` | + + +## The result type + +A lot of lints require checks that go beyond what the pattern syntax described +above can express. For example, a lint might want to check whether a node was +created as part of a macro expansion or whether there's no comment above a node. +Another example would be a lint that wants to match two nodes that have the same +value (as needed by lints like `almost_swapped`). Instead of allowing users to +write these checks into the pattern directly (which might make patterns hard to +read), the proposed solution allows users to assign names to parts of a pattern +expression. When matching a pattern against a syntax tree node, the return value +will contain references to all nodes that were matched by these named +subpatterns. This is similar to capture groups in regular expressions. + +For example, given the following pattern + +```rust +pattern!{ + // matches character literals + my_pattern: Expr = + Lit(Char(_#val_inner)#val)#val_outer +} +``` + +one could get references to the nodes that matched the subpatterns in the +following way: + +```rust +... +fn check_expr(expr: &syntax::ast::Expr) { + if let Some(result) = my_pattern(expr) { + result.val_inner // type: &char + result.val // type: &syntax::ast::Lit + result.val_outer // type: &syntax::ast::Expr + } +} +``` + +The types in the `result` struct depend on the pattern. For example, the +following pattern + +```rust +pattern!{ + // matches arrays of character literals + my_pattern_seq: Expr = + Array( Lit(_)*#foo ) +} +``` + +matches arrays that consist of any number of literal expressions. Because those +expressions are named `foo`, the result struct contains a `foo` attribute which +is a vector of expressions: + +```rust +... +if let Some(result) = my_pattern_seq(expr) { + result.foo // type: Vec<&syntax::ast::Expr> +} +``` + +Another result type occurs when a name is only defined in one branch of an +alternation: + +```rust +pattern!{ + // matches if expression is a boolean or integer literal + my_pattern_alt: Expr = + Lit( Bool(_#bar) | Int(_) ) +} +``` + +In the pattern above, the `bar` name is only defined if the pattern matches a +boolean literal. If it matches an integer literal, the name isn't set. To +account for this, the result struct's `bar` attribute is an option type: + +```rust +... +if let Some(result) = my_pattern_alt(expr) { + result.bar // type: Option<&bool> +} +``` + +It's also possible to use a name in multiple alternation branches if they have +compatible types: + +```rust +pattern!{ + // matches if expression is a boolean or integer literal + my_pattern_mult: Expr = + Lit(_#baz) | Array( Lit(_#baz) ) +} +... +if let Some(result) = my_pattern_mult(expr) { + result.baz // type: &syntax::ast::Lit +} +``` + +Named submatches are a **flat** namespace and this is intended. In the example +above, two different sub-structures are assigned to a flat name. I expect that +for most lints, a flat namespace is sufficient and easier to work with than a +hierarchical one. + +#### Two stages + +Using named subpatterns, users can write lints in two stages. First, a coarse +selection of possible matches is produced by the pattern syntax. In the second +stage, the named subpattern references can be used to do additional tests like +asserting that a node hasn't been created as part of a macro expansion. + +## Implementing clippy lints using patterns + +As a "real-world" example, I re-implemented the `collapsible_if` lint using +patterns. The code can be found +[here](https://github.com/fkohlgrueber/rust-clippy-pattern/blob/039b07ecccaf96d6aa7504f5126720d2c9cceddd/clippy_lints/src/collapsible_if.rs#L88-L163). +The pattern-based version passes all test cases that were written for +`collapsible_if`. + + +# Reference-level explanation + +## Overview + +The following diagram shows the dependencies between the main parts of the +proposed solution: + +``` + Pattern syntax + | + | parsing / lowering + v + PatternTree + ^ + | + | + IsMatch trait + | + | + +---------------+-----------+---------+ + | | | | + v v v v + syntax::ast rustc::hir syn ... +``` + +The pattern syntax described in the previous section is parsed / lowered into +the so-called *PatternTree* data structure that represents a valid syntax tree +pattern. Matching a *PatternTree* against an actual syntax tree (e.g. rust ast / +hir or the syn ast, ...) is done using the *IsMatch* trait. + +The *PatternTree* and the *IsMatch* trait are introduced in more detail in the +following sections. + +## PatternTree + +The core data structure of this RFC is the **PatternTree**. + +It's a data structure similar to rust's AST / HIR, but with the following +differences: + +- The PatternTree doesn't contain parsing information like `Span`s +- The PatternTree can represent alternatives, sequences and optionals + +The code below shows a simplified version of the current PatternTree: + +> Note: The current implementation can be found +> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/pattern_tree.rs#L50-L96). + + +```rust +pub enum Expr { + Lit(Alt), + Array(Seq), + Block_(Alt), + If(Alt, Alt, Opt), + IfLet( + Alt, + Opt, + ), +} + +pub enum Lit { + Char(Alt), + Bool(Alt), + Int(Alt), +} + +pub enum Stmt { + Expr(Alt), + Semi(Alt), +} + +pub enum BlockType { + Block(Seq), +} +``` + +The `Alt`, `Seq` and `Opt` structs look like these: + +> Note: The current implementation can be found +> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60). + +```rust +pub enum Alt { + Any, + Elmt(Box), + Alt(Box, Box), + Named(Box, ...) +} + +pub enum Opt { + Any, // anything, but not None + Elmt(Box), + None, + Alt(Box, Box), + Named(Box, ...) +} + +pub enum Seq { + Any, + Empty, + Elmt(Box), + Repeat(Box, RepeatRange), + Seq(Box, Box), + Alt(Box, Box), + Named(Box, ...) +} + +pub struct RepeatRange { + pub start: usize, + pub end: Option // exclusive +} +``` + +## Parsing / Lowering + +The input of a `pattern!` macro call is parsed into a `ParseTree` first and then +lowered to a `PatternTree`. + +Valid patterns depend on the *PatternTree* definitions. For example, the pattern +`Lit(Bool(_)*)` isn't valid because the parameter type of the `Lit` variant of +the `Expr` enum is `Any` and therefore doesn't support repetition (`*`). As +another example, `Array( Lit(_)* )` is a valid pattern because the parameter of +`Array` is of type `Seq` which allows sequences and repetitions. + +> Note: names in the pattern syntax correspond to *PatternTree* enum +> **variants**. For example, the `Lit` in the pattern above refers to the `Lit` +> variant of the `Expr` enum (`Expr::Lit`), not the `Lit` enum. + +## The IsMatch Trait + +The pattern syntax and the *PatternTree* are independant of specific syntax tree +implementations (rust ast / hir, syn, ...). When looking at the different +pattern examples in the previous sections, it can be seen that the patterns +don't contain any information specific to a certain syntax tree implementation. +In contrast, clippy lints currently match against ast / hir syntax tree nodes +and therefore directly depend on their implementation. + +The connection between the *PatternTree* and specific syntax tree +implementations is the `IsMatch` trait. It defines how to match *PatternTree* +nodes against specific syntax tree nodes. A simplified implementation of the +`IsMatch` trait is shown below: + +```rust +pub trait IsMatch { + fn is_match(&self, other: &'o O) -> bool; +} +``` + +This trait needs to be implemented on each enum of the *PatternTree* (for the +corresponding syntax tree types). For example, the `IsMatch` implementation for +matching `ast::LitKind` against the *PatternTree's* `Lit` enum might look like +this: + +```rust +impl IsMatch for Lit { + fn is_match(&self, other: &ast::LitKind) -> bool { + match (self, other) { + (Lit::Char(i), ast::LitKind::Char(j)) => i.is_match(j), + (Lit::Bool(i), ast::LitKind::Bool(j)) => i.is_match(j), + (Lit::Int(i), ast::LitKind::Int(j, _)) => i.is_match(j), + _ => false, + } + } +} +``` + +All `IsMatch` implementations for matching the current *PatternTree* against +`syntax::ast` can be found +[here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/ast_match.rs). + + +# Drawbacks + +#### Performance + +The pattern matching code is currently not optimized for performance, so it +might be slower than hand-written matching code. Additionally, the two-stage +approach (matching against the coarse pattern first and checking for additional +properties later) might be slower than the current practice of checking for +structure and additional properties in one pass. For example, the following lint + +```rust +pattern!{ + pat_if_without_else: Expr = + If( + _, + Block( + Expr( If(_, _, ())#inner ) + | Semi( If(_, _, ())#inner ) + )#then, + () + ) +} +... +fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if let Some(result) = pat_if_without_else(expr) { + if !block_starts_with_comment(cx, result.then) { + ... + } +} +``` + +first matches against the pattern and then checks that the `then` block doesn't +start with a comment. Using clippy's current approach, it's possible to check +for these conditions earlier: + +```rust +fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if_chain! { + if let ast::ExprKind::If(ref check, ref then, None) = expr.node; + if !block_starts_with_comment(cx, then); + if let Some(inner) = expr_block(then); + if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node; + then { + ... + } + } +} +``` + +Whether or not this causes performance regressions depends on actual patterns. +If it turns out to be a problem, the pattern matching algorithms could be +extended to allow "early filtering" (see the [Early Filtering](#early-filtering) +section in Future Possibilities). + +That being said, I don't see any conceptual limitations regarding pattern +matching performance. + +#### Applicability + +Even though I'd expect that a lot of lints can be written using the proposed +pattern syntax, it's unlikely that all lints can be expressed using patterns. I +suspect that there will still be lints that need to be implemented by writing +custom pattern matching code. This would lead to mix within clippy's codebase +where some lints are implemented using patterns and others aren't. This +inconsistency might be considered a drawback. + + +# Rationale and alternatives + +Specifying lints using syntax tree patterns has a couple of advantages compared +to the current approach of manually writing matching code. First, syntax tree +patterns allow users to describe patterns in a simple and expressive way. This +makes it easier to write new lints for both novices and experts and also makes +reading / modifying existing lints simpler. + +Another advantage is that lints are independent of specific syntax tree +implementations (e.g. AST / HIR, ...). When these syntax tree implementations +change, only the `IsMatch` trait implementations need to be adapted and existing +lints can remain unchanged. This also means that if the `IsMatch` trait +implementations were integrated into the compiler, updating the `IsMatch` +implementations would be required for the compiler to compile successfully. This +could reduce the number of times clippy breaks because of changes in the +compiler. Another advantage of the pattern's independence is that converting an +`EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole +pattern matching code. In fact, the pattern might work just fine without any +adaptions. + + +## Alternatives + +### Rust-like pattern syntax + +The proposed pattern syntax requires users to know the structure of the +`PatternTree` (which is very similar to the AST's / HIR's structure) and also +the pattern syntax. An alternative would be to introduce a pattern syntax that +is similar to actual Rust syntax (probably like the `quote!` macro). For +example, a pattern that matches `if` expressions that have `false` in their +condition could look like this: + +```rust +if false { + #[*] +} +``` + +#### Problems + +Extending Rust syntax (which is quite complex by itself) with additional syntax +needed for specifying patterns (alternations, sequences, repetisions, named +submatches, ...) might become difficult to read and really hard to parse +properly. + +For example, a pattern that matches a binary operation that has `0` on both +sides might look like this: + +``` +0 #[*:BinOpKind] 0 +``` + +Now consider this slightly more complex example: + +``` +1 + 0 #[*:BinOpKind] 0 +``` + +The parser would need to know the precedence of `#[*:BinOpKind]` because it +affects the structure of the resulting AST. `1 + 0 + 0` is parsed as `(1 + 0) + +0` while `1 + 0 * 0` is parsed as `1 + (0 * 0)`. Since the pattern could be any +`BinOpKind`, the precedence cannot be known in advance. + +Another example of a problem would be named submatches. Take a look at this +pattern: + +```rust +fn test() { + 1 #foo +} +``` + +Which node is `#foo` referring to? `int`, `ast::Lit`, `ast::Expr`, `ast::Stmt`? +Naming subpatterns in a rust-like syntax is difficult because a lot of AST nodes +don't have a syntactic element that can be used to put the name tag on. In these +situations, the only sensible option would be to assign the name tag to the +outermost node (`ast::Stmt` in the example above), because the information of +all child nodes can be retrieved through the outermost node. The problem with +this then would be that accessing inner nodes (like `ast::Lit`) would again +require manual pattern matching. + +In general, Rust syntax contains a lot of code structure implicitly. This +structure is reconstructed during parsing (e.g. binary operations are +reconstructed using operator precedence and left-to-right) and is one of the +reasons why parsing is a complex task. The advantage of this approach is that +writing code is simpler for users. + +When writing *syntax tree patterns*, each element of the hierarchy might have +alternatives, repetitions, etc.. Respecting that while still allowing +human-friendly syntax that contains structure implicitly seems to be really +complex, if not impossible. + +Developing such a syntax would also require to maintain a custom parser that is +at least as complex as the Rust parser itself. Additionally, future changes in +the Rust syntax might be incompatible with such a syntax. + +In summary, I think that developing such a syntax would introduce a lot of +complexity to solve a relatively minor problem. + +The issue of users not knowing about the *PatternTree* structure could be solved +by a tool that, given a rust program, generates a pattern that matches only this +program (similar to the clippy author lint). + +For some simple cases (like the first example above), it might be possible to +successfully mix Rust and pattern syntax. This space could be further explored +in a future extension. + +# Prior art + +The pattern syntax is heavily inspired by regular expressions (repetitions, +alternatives, sequences, ...). + +From what I've seen until now, other linters also implement lints that directly +work on syntax tree data structures, just like clippy does currently. I would +therefore consider the pattern syntax to be *new*, but please correct me if I'm +wrong. + +# Unresolved questions + +#### How to handle multiple matches? + +When matching a syntax tree node against a pattern, there are possibly multiple +ways in which the pattern can be matched. A simple example of this would be the +following pattern: + +```rust +pattern!{ + my_pattern: Expr = + Array( _* Lit(_)+#literals) +} +``` + +This pattern matches arrays that end with at least one literal. Now given the +array `[x, 1, 2]`, should `1` be matched as part of the `_*` or the `Lit(_)+` +part of the pattern? The difference is important because the named submatch +`#literals` would contain 1 or 2 elements depending how the pattern is matched. +In regular expressions, this problem is solved by matching "greedy" by default +and "non-greedy" optionally. + +I haven't looked much into this yet because I don't know how relevant it is for +most lints. The current implementation simply returns the first match it finds. + +# Future possibilities + +#### Implement rest of Rust Syntax + +The current project only implements a small part of the Rust syntax. In the +future, this should incrementally be extended to more syntax to allow +implementing more lints. Implementing more of the Rust syntax requires extending +the `PatternTree` and `IsMatch` implementations, but should be relatively +straight-forward. + +#### Early filtering + +As described in the *Drawbacks/Performance* section, allowing additional checks +during the pattern matching might be beneficial. + +The pattern below shows how this could look like: + +```rust +pattern!{ + pat_if_without_else: Expr = + If( + _, + Block( + Expr( If(_, _, ())#inner ) + | Semi( If(_, _, ())#inner ) + )#then, + () + ) + where + !in_macro(#then.span); +} +``` + +The difference compared to the currently proposed two-stage filtering is that +using early filtering, the condition (`!in_macro(#then.span)` in this case) +would be evaluated as soon as the `Block(_)#then` was matched. + +Another idea in this area would be to introduce a syntax for backreferences. +They could be used to require that multiple parts of a pattern should match the +same value. For example, the `assign_op_pattern` lint that searches for `a = a +op b` and recommends changing it to `a op= b` requires that both occurrances of +`a` are the same. Using `=#...` as syntax for backreferences, the lint could be +implemented like this: + +```rust +pattern!{ + assign_op_pattern: Expr = + Assign(_#target, Binary(_, =#target, _) +} +``` + +#### Match descendant + +A lot of lints currently implement custom visitors that check whether any +subtree (which might not be a direct descendant) of the current node matches +some properties. This cannot be expressed with the proposed pattern syntax. +Extending the pattern syntax to allow patterns like "a function that contains at +least two return statements" could be a practical addition. + +#### Negation operator for alternatives + +For patterns like "a literal that is not a boolean literal" one currently needs +to list all alternatives except the boolean case. Introducing a negation +operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern +would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three +literal types are implemented). + +#### Functional composition + +Patterns currently don't have any concept of composition. This leads to +repetitions within patterns. For example, one of the collapsible-if patterns +currently has to be written like this: + +```rust +pattern!{ + pat_if_else: Expr = + If( + _, + _, + Block_( + Block( + Expr((If(_, _, _?) | IfLet(_, _?))#else_) | + Semi((If(_, _, _?) | IfLet(_, _?))#else_) + )#block_inner + )#block + ) | + IfLet( + _, + Block_( + Block( + Expr((If(_, _, _?) | IfLet(_, _?))#else_) | + Semi((If(_, _, _?) | IfLet(_, _?))#else_) + )#block_inner + )#block + ) +} +``` + +If patterns supported defining functions of subpatterns, the code could be +simplified as follows: + +```rust +pattern!{ + fn expr_or_semi(expr: Expr) -> Stmt { + Expr(expr) | Semi(expr) + } + fn if_or_if_let(then: Block, else: Opt) -> Expr { + If(_, then, else) | IfLet(then, else) + } + pat_if_else: Expr = + if_or_if_let( + _, + Block_( + Block( + expr_or_semi( if_or_if_let(_, _?)#else_ ) + )#block_inner + )#block + ) +} +``` + +Additionally, common patterns like `expr_or_semi` could be shared between +different lints. + +#### Clippy Pattern Author + +Another improvement could be to create a tool that, given some valid Rust +syntax, generates a pattern that matches this syntax exactly. This would make +starting to write a pattern easier. A user could take a look at the patterns +generated for a couple of Rust code examples and use that information to write a +pattern that matches all of them. + +This is similar to clippy's author lint. + +#### Supporting other syntaxes + +Most of the proposed system is language-agnostic. For example, the pattern +syntax could also be used to describe patterns for other programming languages. + +In order to support other languages' syntaxes, one would need to implement +another `PatternTree` that sufficiently describes the languages' AST and +implement `IsMatch` for this `PatternTree` and the languages' AST. + +One aspect of this is that it would even be possible to write lints that work on +the pattern syntax itself. For example, when writing the following pattern + + +```rust +pattern!{ + my_pattern: Expr = + Array( Lit(Bool(false)) Lit(Bool(false)) ) +} +``` + +a lint that works on the pattern syntax's AST could suggest using this pattern +instead: + +```rust +pattern!{ + my_pattern: Expr = + Array( Lit(Bool(false)){2} ) +} +``` + +In the future, clippy could use this system to also provide lints for custom +syntaxes like those found in macros. diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 9e15f1504fa9..ec7f1dd0d846 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -120,7 +120,7 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let new_lint = if enable_msrv { format!( - "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv)));\n ", + "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv())));\n ", lint_pass = lint.pass, ctor_arg = if lint.pass == "late" { "_" } else { "" }, module_name = lint.name, @@ -238,10 +238,9 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result.push_str(&if enable_msrv { formatdoc!( r#" - use clippy_utils::msrvs; + use clippy_utils::msrvs::{{self, Msrv}}; {pass_import} use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; - use rustc_semver::RustcVersion; use rustc_session::{{declare_tool_lint, impl_lint_pass}}; "# @@ -263,12 +262,12 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { formatdoc!( r#" pub struct {name_camel} {{ - msrv: Option, + msrv: Msrv, }} impl {name_camel} {{ #[must_use] - pub fn new(msrv: Option) -> Self {{ + pub fn new(msrv: Msrv) -> Self {{ Self {{ msrv }} }} }} @@ -357,15 +356,14 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R let _ = writedoc!( lint_file_contents, r#" - use clippy_utils::{{meets_msrv, msrvs}}; + use clippy_utils::msrvs::{{self, Msrv}}; use rustc_lint::{{{context_import}, LintContext}}; - use rustc_semver::RustcVersion; use super::{name_upper}; // TODO: Adjust the parameters as necessary - pub(super) fn check(cx: &{context_import}, msrv: Option) {{ - if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{ + pub(super) fn check(cx: &{context_import}, msrv: &Msrv) {{ + if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{ return; }} todo!(); diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_letter_range.rs index df92579a85df..52beaf504a4e 100644 --- a/clippy_lints/src/almost_complete_letter_range.rs +++ b/clippy_lints/src/almost_complete_letter_range.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{trim_span, walk_span_to_context}; -use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -33,10 +32,10 @@ declare_clippy_lint! { impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]); pub struct AlmostCompleteLetterRange { - msrv: Option, + msrv: Msrv, } impl AlmostCompleteLetterRange { - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -46,7 +45,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange { let ctxt = e.span.ctxt(); let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt) && let Some(end) = walk_span_to_context(end.span, ctxt) - && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) + && self.msrv.meets(msrvs::RANGE_INCLUSIVE) { Some((trim_span(cx.sess().source_map(), start.between(end)), "..=")) } else { @@ -60,7 +59,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange { if let PatKind::Range(Some(start), Some(end), kind) = &p.kind && matches!(kind.node, RangeEnd::Excluded) { - let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) { + let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) { "..=" } else { "..." diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 724490fb4959..ccf82f132f4e 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -63,12 +63,12 @@ const KNOWN_CONSTS: [(f64, &str, usize, Option); 19] = [ ]; pub struct ApproxConstant { - msrv: Option, + msrv: Msrv, } impl ApproxConstant { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } @@ -87,7 +87,7 @@ impl ApproxConstant { let s = s.as_str(); if s.parse::().is_ok() { for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { - if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) { + if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| self.msrv.meets(msrv)) { span_lint_and_help( cx, APPROX_CONSTANT, diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 018f10f25886..0710ac0bb0a7 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -2,9 +2,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, macro_backtrace}; -use clippy_utils::msrvs; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_errors::Applicability; @@ -14,7 +13,6 @@ 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_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; @@ -599,7 +597,7 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { } pub struct EarlyAttributes { - pub msrv: Option, + pub msrv: Msrv, } impl_lint_pass!(EarlyAttributes => [ @@ -614,7 +612,7 @@ impl EarlyLintPass for EarlyAttributes { } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { - check_deprecated_cfg_attr(cx, attr, self.msrv); + check_deprecated_cfg_attr(cx, attr, &self.msrv); check_mismatched_target_os(cx, attr); } @@ -654,9 +652,9 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It } } -fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option) { +fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { if_chain! { - if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES); + if msrv.meets(msrvs::TOOL_ATTRIBUTES); // check cfg_attr if attr.has_name(sym::cfg_attr); if let Some(items) = attr.meta_item_list(); diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 3f1edabe6c50..442262983337 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use super::CAST_ABS_TO_UNSIGNED; @@ -15,9 +14,9 @@ pub(super) fn check( cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: Option, + msrv: &Msrv, ) { - if meets_msrv(msrv, msrvs::UNSIGNED_ABS) + if msrv.meets(msrvs::UNSIGNED_ABS) && let ty::Int(from) = cast_from.kind() && let ty::Uint(to) = cast_to.kind() && let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 13c403234dad..cf07e050ccce 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_constant; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_isize_or_usize; -use clippy_utils::{in_constant, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use rustc_semver::RustcVersion; use super::{utils, CAST_LOSSLESS}; @@ -16,7 +16,7 @@ pub(super) fn check( cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: Option, + msrv: &Msrv, ) { if !should_lint(cx, expr, cast_from, cast_to, msrv) { return; @@ -57,13 +57,7 @@ pub(super) fn check( ); } -fn should_lint( - cx: &LateContext<'_>, - expr: &Expr<'_>, - cast_from: Ty<'_>, - cast_to: Ty<'_>, - msrv: Option, -) -> bool { +fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). if in_constant(cx, expr.hir_id) { return false; @@ -89,7 +83,7 @@ fn should_lint( }; !is_isize_or_usize(cast_from) && from_nbits < to_nbits }, - (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true, + (false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(msrvs::FROM_BOOL) => true, (_, _) => { matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) }, diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index adbcfd3189b7..a6376484914b 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -118,12 +118,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, }; let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); - let cast_from_ptr_size = def.repr().int.map_or(true, |ty| { - matches!( - ty, - IntegerType::Pointer(_), - ) - }); + let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),)); let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) { (false, false) if from_nbits > to_nbits => "", (true, false) if from_nbits > to_nbits => "", diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index d31d10d22b92..e862f13e69fc 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,16 +1,16 @@ -use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{diagnostics::span_lint_and_then, source}; use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut}; -use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv) { // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist - if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) { + if !msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) { return; } diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 284ef165998a..627b795d6edd 100644 --- a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use super::CAST_SLICE_FROM_RAW_PARTS; @@ -25,15 +25,9 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option { } } -pub(super) fn check( - cx: &LateContext<'_>, - expr: &Expr<'_>, - cast_expr: &Expr<'_>, - cast_to: Ty<'_>, - msrv: Option, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) { if_chain! { - if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS); + if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS); if let ty::RawPtr(ptrty) = cast_to.kind(); if let ty::Slice(_) = ptrty.ty.kind(); if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind; diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 7148b5e6ebf4..c6d505c4a181 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -21,11 +21,11 @@ mod ptr_as_ptr; mod unnecessary_cast; mod utils; -use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs}; +use clippy_utils::is_hir_ty_cfg_dependant; +use clippy_utils::msrvs::{self, Msrv}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -648,12 +648,12 @@ declare_clippy_lint! { } pub struct Casts { - msrv: Option, + msrv: Msrv, } impl Casts { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -686,7 +686,7 @@ impl_lint_pass!(Casts => [ impl<'tcx> LateLintPass<'tcx> for Casts { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !in_external_macro(cx.sess(), expr.span) { - ptr_as_ptr::check(cx, expr, self.msrv); + ptr_as_ptr::check(cx, expr, &self.msrv); } if expr.span.from_expansion() { @@ -705,7 +705,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) { return; } - cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv); + cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); @@ -717,16 +717,16 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); - cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } as_underscore::check(cx, expr, cast_to_hir); - if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) { + if self.msrv.meets(msrvs::BORROW_AS_PTR) { borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir); } } @@ -734,8 +734,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_ref_to_mut::check(cx, expr); cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); - ptr_as_ptr::check(cx, expr, self.msrv); - cast_slice_different_sizes::check(cx, expr, self.msrv); + ptr_as_ptr::check(cx, expr, &self.msrv); + cast_slice_different_sizes::check(cx, expr, &self.msrv); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index b9509ca656f7..15ffb00da88b 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -1,19 +1,18 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, TypeAndMut}; -use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option) { - if !meets_msrv(msrv, msrvs::POINTER_CAST) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { + if !msrv.meets(msrvs::POINTER_CAST) { return; } diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index c8596987e4d7..7e23318076cf 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet_opt; +use clippy_utils::{get_parent_expr, path_to_local}; use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; @@ -75,13 +75,26 @@ pub(super) fn check<'tcx>( } if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { + if let Some(id) = path_to_local(cast_expr) + && let Some(span) = cx.tcx.hir().opt_span(id) + && span.ctxt() != cast_expr.span.ctxt() + { + // Binding context is different than the identifiers context. + // Weird macro wizardry could be involved here. + return false; + } + span_lint_and_sugg( cx, UNNECESSARY_CAST, expr.span, &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", - cast_str, + if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(..))) { + format!("{{ {cast_str} }}") + } else { + cast_str + }, Applicability::MachineApplicable, ); return true; diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 78e9921f036f..9102a89e3772 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -1,14 +1,14 @@ //! lint on manually implemented checked conversions that could be transformed into `try_from` use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq}; +use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; use if_chain::if_chain; 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_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -37,12 +37,12 @@ declare_clippy_lint! { } pub struct CheckedConversions { - msrv: Option, + msrv: Msrv, } impl CheckedConversions { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -51,7 +51,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::TRY_FROM) { + if !self.msrv.meets(msrvs::TRY_FROM) { return; } diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 90430b71a0ef..b38e09dc09f4 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -160,11 +160,13 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind; // Prevent triggering on `if c { if let a = b { .. } }`. if !matches!(check_inner.kind, ast::ExprKind::Let(..)); - if expr.span.ctxt() == inner.span.ctxt(); + let ctxt = expr.span.ctxt(); + if inner.span.ctxt() == ctxt; then { span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| { - let lhs = Sugg::ast(cx, check, ".."); - let rhs = Sugg::ast(cx, check_inner, ".."); + let mut app = Applicability::MachineApplicable; + let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app); + let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app); diag.span_suggestion( expr.span, "collapse nested if block", @@ -173,7 +175,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & lhs.and(&rhs), snippet_block(cx, content.span, "..", Some(expr.span)), ), - Applicability::MachineApplicable, // snippet + app, // snippet ); }); } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 0d3fc43a6443..e4d76f07d6b4 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -177,6 +177,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO, crate::from_str_radix_10::FROM_STR_RADIX_10_INFO, crate::functions::DOUBLE_MUST_USE_INFO, + crate::functions::MISNAMED_GETTERS_INFO, crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, @@ -583,6 +584,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::types::TYPE_COMPLEXITY_INFO, crate::types::VEC_BOX_INFO, crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO, + crate::undocumented_unsafe_blocks::UNNECESSARY_SAFETY_COMMENT_INFO, crate::unicode::INVISIBLE_CHARACTERS_INFO, crate::unicode::NON_ASCII_LITERAL_INFO, crate::unicode::UNICODE_NOT_NFC_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 47ea98956be2..38329659e02b 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; use clippy_utils::{ - fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local, - walk_to_expr_usage, + fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage, }; + use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; @@ -28,7 +29,6 @@ use rustc_middle::ty::{ self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults, }; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt as _; @@ -181,12 +181,12 @@ pub struct Dereferencing<'tcx> { possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, // `IntoIterator` for arrays requires Rust 1.53. - msrv: Option, + msrv: Msrv, } impl<'tcx> Dereferencing<'tcx> { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Dereferencing::default() @@ -286,26 +286,27 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv); + let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv); match kind { RefOp::Deref => { + let sub_ty = typeck.expr_ty(sub_expr); if let Position::FieldAccess { name, of_union: false, } = position - && !ty_contains_field(typeck.expr_ty(sub_expr), name) + && !ty_contains_field(sub_ty, name) { self.state = Some(( State::ExplicitDerefField { name }, StateData { span: expr.span, hir_id: expr.hir_id, position }, )); - } else if position.is_deref_stable() { + } else if position.is_deref_stable() && sub_ty.is_ref() { self.state = Some(( State::ExplicitDeref { mutability: None }, StateData { span: expr.span, hir_id: expr.hir_id, position }, )); } - } + }, RefOp::Method(target_mut) if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) && position.lint_explicit_deref() => @@ -320,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { StateData { span: expr.span, hir_id: expr.hir_id, - position + position, }, )); }, @@ -394,7 +395,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { msg, snip_expr, }), - StateData { span: expr.span, hir_id: expr.hir_id, position }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + position, + }, )); } else if position.is_deref_stable() // Auto-deref doesn't combine with other adjustments @@ -406,7 +411,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { StateData { span: expr.span, hir_id: expr.hir_id, - position + position, }, )); } @@ -698,7 +703,7 @@ fn walk_parents<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, e: &'tcx Expr<'_>, - msrv: Option, + msrv: &Msrv, ) -> (Position, &'tcx [Adjustment<'tcx>]) { let mut adjustments = [].as_slice(); let mut precedence = 0i8; @@ -862,7 +867,11 @@ fn walk_parents<'tcx>( } && impl_ty.is_ref() && let infcx = cx.tcx.infer_ctxt().build() && infcx - .type_implements_trait(trait_id, [impl_ty.into()].into_iter().chain(subs.iter().copied()), cx.param_env) + .type_implements_trait( + trait_id, + [impl_ty.into()].into_iter().chain(subs.iter().copied()), + cx.param_env, + ) .must_apply_modulo_regions() { return Some(Position::MethodReceiverRefImpl) @@ -1078,7 +1087,7 @@ fn needless_borrow_impl_arg_position<'tcx>( param_ty: ParamTy, mut expr: &Expr<'tcx>, precedence: i8, - msrv: Option, + msrv: &Msrv, ) -> Position { let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); @@ -1178,7 +1187,7 @@ fn needless_borrow_impl_arg_position<'tcx>( && let ty::Param(param_ty) = trait_predicate.self_ty().kind() && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack() && ty.is_array() - && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR) + && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) { return false; } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index d870e0ceef47..9e596ca8157e 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -14,8 +14,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, - Ty, TyCtxt, + self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, + TraitRef, Ty, TyCtxt, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index ae5f9424b232..cdc23a4d2273 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -253,7 +253,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.66.0"] pub UNNECESSARY_SAFETY_DOC, - style, + restriction, "`pub fn` or `pub trait` with `# Safety` docs" } diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index f34cbee03558..3543910c3b55 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::{self, Ty, TypeVisitable}; +use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -125,7 +125,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if let Some(mut snippet) = snippet_opt(cx, callee.span) { if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait() && let args = cx.tcx.erase_late_bound_regions(substs.as_closure().sig()).inputs() - && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &args.iter().copied().map(Into::into).collect::>()) + && implements_trait( + cx, + callee_ty.peel_refs(), + fn_mut_id, + &args.iter().copied().map(Into::into).collect::>(), + ) && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)) { // Mutable closure is used after current expr; we cannot consume it. @@ -152,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if check_sig(cx, closure_ty, call_ty); then { span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| { - let name = get_ufcs_type_name(cx, method_def_id); + let name = get_ufcs_type_name(cx, method_def_id, substs); diag.span_suggestion( expr.span, "replace the closure with the method itself", @@ -222,7 +227,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tc cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig) } -fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String { +fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs: SubstsRef<'tcx>) -> String { let assoc_item = cx.tcx.associated_item(method_def_id); let def_id = assoc_item.container_id(cx.tcx); match assoc_item.container { @@ -231,6 +236,15 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String { let ty = cx.tcx.type_of(def_id); match ty.kind() { ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()), + ty::Array(..) + | ty::Dynamic(..) + | ty::Never + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Slice(_) + | ty::Tuple(_) => { + format!("<{}>", EarlyBinder(ty).subst(cx.tcx, substs)) + }, _ => ty.to_string(), } }, diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 407dd1b39575..9c8b0d076dfd 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -7,21 +7,34 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// `exit()` terminates the program and doesn't provide a - /// stack trace. + /// Detects calls to the `exit()` function which terminates the program. /// /// ### Why is this bad? - /// Ideally a program is terminated by finishing + /// Exit terminates the program at the location it is called. For unrecoverable + /// errors `panics` should be used to provide a stacktrace and potentualy other + /// information. A normal termination or one with an error code should happen in /// the main function. /// /// ### Example - /// ```ignore + /// ``` /// std::process::exit(0) /// ``` + /// + /// Use instead: + /// + /// ```ignore + /// // To provide a stacktrace and additional information + /// panic!("message"); + /// + /// // or a main method with a return + /// fn main() -> Result<(), i32> { + /// Ok(()) + /// } + /// ``` #[clippy::version = "1.41.0"] pub EXIT, restriction, - "`std::process::exit` is called, terminating the program" + "detects `std::process::exit` calls" } declare_lint_pass!(Exit => [EXIT]); diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index f0fe845d3303..111b624f5299 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,19 +1,22 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; +use clippy_utils::is_diag_trait_item; +use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred}; use clippy_utils::macros::{ is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage, }; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; use if_chain::if_chain; use itertools::Itertools; -use rustc_errors::Applicability; +use rustc_errors::{ + Applicability, + SuggestionStyle::{CompletelyHidden, ShowCode}, +}; use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::DefId; use rustc_span::edition::Edition::Edition2021; @@ -103,19 +106,25 @@ declare_clippy_lint! { /// format!("{var:.prec$}"); /// ``` /// - /// ### Known Problems - /// - /// There may be a false positive if the format string is expanded from certain proc macros: - /// - /// ```ignore - /// println!(indoc!("{}"), var); + /// If allow-mixed-uninlined-format-args is set to false in clippy.toml, + /// the following code will also trigger the lint: + /// ```rust + /// # let var = 42; + /// format!("{} {}", var, 1+2); + /// ``` + /// Use instead: + /// ```rust + /// # let var = 42; + /// format!("{var} {}", 1+2); /// ``` /// + /// ### Known Problems + /// /// If a format string contains a numbered argument that cannot be inlined /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.65.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } @@ -158,13 +167,17 @@ impl_lint_pass!(FormatArgs => [ ]); pub struct FormatArgs { - msrv: Option, + msrv: Msrv, + ignore_mixed: bool, } impl FormatArgs { #[must_use] - pub fn new(msrv: Option) -> Self { - Self { msrv } + pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { + Self { + msrv, + ignore_mixed: allow_mixed_uninlined_format_args, + } } } @@ -188,8 +201,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); check_to_string_in_format_args(cx, name, arg.param.value); } - if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id); + if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed); } } } @@ -267,7 +280,13 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { } } -fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) { +fn check_uninlined_args( + cx: &LateContext<'_>, + args: &FormatArgsExpn<'_>, + call_site: Span, + def_id: DefId, + ignore_mixed: bool, +) { if args.format_string.span.from_expansion() { return; } @@ -282,14 +301,13 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si // we cannot remove any other arguments in the format string, // because the index numbers might be wrong after inlining. // Example of an un-inlinable format: print!("{}{1}", foo, 2) - if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() { + if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() { return; } - // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 - if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) { - return; - } + // multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 + // in those cases, make the code suggestion hidden + let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)); span_lint_and_then( cx, @@ -297,12 +315,22 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si call_site, "variables can be used directly in the `format!` string", |diag| { - diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable); + diag.multipart_suggestion_with_style( + "change this to", + fixes, + Applicability::MachineApplicable, + if multiline_fix { CompletelyHidden } else { ShowCode }, + ); }, ); } -fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool { +fn check_one_arg( + args: &FormatArgsExpn<'_>, + param: &FormatParam<'_>, + fixes: &mut Vec<(Span, String)>, + ignore_mixed: bool, +) -> bool { if matches!(param.kind, Implicit | Starred | Named(_) | Numbered) && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind && let [segment] = path.segments @@ -317,8 +345,10 @@ fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut fixes.push((arg_span, String::new())); true // successful inlining, continue checking } else { - // if we can't inline a numbered argument, we can't continue - param.kind != Numbered + // Do not continue inlining (return false) in case + // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)` + // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already + param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_))) } } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 8b24a4962fb2..8621504c1b47 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::path_def_id; use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs, path_def_id}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_path, Visitor}; use rustc_hir::{ @@ -10,7 +11,6 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, sym}; use rustc_span::{Span, Symbol}; @@ -49,12 +49,12 @@ declare_clippy_lint! { } pub struct FromOverInto { - msrv: Option, + msrv: Msrv, } impl FromOverInto { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { FromOverInto { msrv } } } @@ -63,7 +63,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { + if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { return; } diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs new file mode 100644 index 000000000000..27acad45ccf7 --- /dev/null +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -0,0 +1,125 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind, Unsafety}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::Span; + +use std::iter; + +use super::MISNAMED_GETTERS; + +pub fn check_fn( + cx: &LateContext<'_>, + kind: FnKind<'_>, + decl: &FnDecl<'_>, + body: &Body<'_>, + span: Span, + _hir_id: HirId, +) { + let FnKind::Method(ref ident, sig) = kind else { + return; + }; + + // Takes only &(mut) self + if decl.inputs.len() != 1 { + return; + } + + let name = ident.name.as_str(); + + let name = match decl.implicit_self { + ImplicitSelfKind::MutRef => { + let Some(name) = name.strip_suffix("_mut") else { + return; + }; + name + }, + ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name, + ImplicitSelfKind::None => return, + }; + + let name = if sig.header.unsafety == Unsafety::Unsafe { + name.strip_suffix("_unchecked").unwrap_or(name) + } else { + name + }; + + // Body must be &(mut) .name + // self_data is not neccessarilly self, to also lint sub-getters, etc… + + let block_expr = if_chain! { + if let ExprKind::Block(block,_) = body.value.kind; + if block.stmts.is_empty(); + if let Some(block_expr) = block.expr; + then { + block_expr + } else { + return; + } + }; + let expr_span = block_expr.span; + + // Accept &, &mut and + let expr = if let ExprKind::AddrOf(_, _, tmp) = block_expr.kind { + tmp + } else { + block_expr + }; + let (self_data, used_ident) = if_chain! { + if let ExprKind::Field(self_data, ident) = expr.kind; + if ident.name.as_str() != name; + then { + (self_data, ident) + } else { + return; + } + }; + + let mut used_field = None; + let mut correct_field = None; + let typeck_results = cx.typeck_results(); + for adjusted_type in iter::once(typeck_results.expr_ty(self_data)) + .chain(typeck_results.expr_adjustments(self_data).iter().map(|adj| adj.target)) + { + let ty::Adt(def,_) = adjusted_type.kind() else { + continue; + }; + + for f in def.all_fields() { + if f.name.as_str() == name { + correct_field = Some(f); + } + if f.name == used_ident.name { + used_field = Some(f); + } + } + } + + let Some(used_field) = used_field else { + // Can happen if the field access is a tuple. We don't lint those because the getter name could not start with a number. + return; + }; + + let Some(correct_field) = correct_field else { + // There is no field corresponding to the getter name. + // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is + return; + }; + + if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) { + let left_span = block_expr.span.until(used_ident.span); + let snippet = snippet(cx, left_span, ".."); + let sugg = format!("{snippet}{name}"); + span_lint_and_then( + cx, + MISNAMED_GETTERS, + span, + "getter function appears to return the wrong field", + |diag| { + diag.span_suggestion(expr_span, "consider using", sugg, Applicability::MaybeIncorrect); + }, + ); + } +} diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index ae0e08334463..91e6ffe64479 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,3 +1,4 @@ +mod misnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; mod result; @@ -260,6 +261,48 @@ declare_clippy_lint! { "function returning `Result` with large `Err` type" } +declare_clippy_lint! { + /// ### What it does + /// Checks for getter methods that return a field that doesn't correspond + /// to the name of the method, when there is a field's whose name matches that of the method. + /// + /// ### Why is this bad? + /// It is most likely that such a method is a bug caused by a typo or by copy-pasting. + /// + /// ### Example + + /// ```rust + /// struct A { + /// a: String, + /// b: String, + /// } + /// + /// impl A { + /// fn a(&self) -> &str{ + /// &self.b + /// } + /// } + + /// ``` + /// Use instead: + /// ```rust + /// struct A { + /// a: String, + /// b: String, + /// } + /// + /// impl A { + /// fn a(&self) -> &str{ + /// &self.a + /// } + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub MISNAMED_GETTERS, + suspicious, + "getter method returning the wrong field" +} + #[derive(Copy, Clone)] pub struct Functions { too_many_arguments_threshold: u64, @@ -286,6 +329,7 @@ impl_lint_pass!(Functions => [ MUST_USE_CANDIDATE, RESULT_UNIT_ERR, RESULT_LARGE_ERR, + MISNAMED_GETTERS, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -301,6 +345,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); + misnamed_getters::check_fn(cx, kind, decl, body, span, hir_id); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index f7e30b051a69..23da145d0382 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -94,7 +94,9 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty if let hir::ItemKind::Enum(ref def, _) = item.kind; then { let variants_size = AdtVariantInfo::new(cx, *adt, subst); - if variants_size[0].size >= large_err_threshold { + if let Some((first_variant, variants)) = variants_size.split_first() + && first_variant.size >= large_err_threshold + { span_lint_and_then( cx, RESULT_LARGE_ERR, @@ -102,11 +104,11 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty "the `Err`-variant returned from this function is very large", |diag| { diag.span_label( - def.variants[variants_size[0].ind].span, + def.variants[first_variant.ind].span, format!("the largest variant contains at least {} bytes", variants_size[0].size), ); - for variant in &variants_size[1..] { + for variant in variants { if variant.size >= large_err_threshold { let variant_def = &def.variants[variant.ind]; diag.span_label( diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index a9425a40f885..61934a914263 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -91,7 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { infcx .err_ctxt() .maybe_note_obligation_cause_for_async_await(db, &obligation); - if let PredicateKind::Clause(Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() { + if let PredicateKind::Clause(Clause::Trait(trait_pred)) = + obligation.predicate.kind().skip_binder() + { db.note(&format!( "`{}` doesn't implement `{}`", trait_pred.self_ty(), diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 0d6718c168a5..9cadaaa493e4 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,14 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{ - contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks, -}; +use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -47,12 +45,12 @@ declare_clippy_lint! { } pub struct IfThenSomeElseNone { - msrv: Option, + msrv: Msrv, } impl IfThenSomeElseNone { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -61,7 +59,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !meets_msrv(self.msrv, msrvs::BOOL_THEN) { + if !self.msrv.meets(msrvs::BOOL_THEN) { return; } @@ -94,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { } else { format!("{{ /* snippet */ {arg_snip} }}") }; - let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { + let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) { "then_some" } else { method_body.insert_str(0, "|| "); diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 0d5099bde6de..cf35b1f175c6 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -1,8 +1,9 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; use if_chain::if_chain; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; @@ -11,7 +12,6 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::Ident, Span}; @@ -47,18 +47,17 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.59.0"] pub INDEX_REFUTABLE_SLICE, - nursery, + pedantic, "avoid indexing on slices which could be destructed" } -#[derive(Copy, Clone)] pub struct IndexRefutableSlice { max_suggested_slice: u64, - msrv: Option, + msrv: Msrv, } impl IndexRefutableSlice { - pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option) -> Self { + pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self { Self { max_suggested_slice: max_suggested_slice_pattern_length, msrv, @@ -74,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr); if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id); - if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS); + if self.msrv.meets(msrvs::SLICE_PATTERNS); let found_slices = find_slice_values(cx, let_pat); if !found_slices.is_empty(); diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 60754b224fc8..dd1b23e7d9d2 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -1,13 +1,11 @@ -use clippy_utils::{ - diagnostics::{self, span_lint_and_sugg}, - meets_msrv, msrvs, source, - sugg::Sugg, - ty, -}; +use clippy_utils::diagnostics::{self, span_lint_and_sugg}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{source_map::Spanned, sym}; @@ -68,12 +66,12 @@ declare_clippy_lint! { } pub struct InstantSubtraction { - msrv: Option, + msrv: Msrv, } impl InstantSubtraction { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -101,7 +99,7 @@ impl LateLintPass<'_> for InstantSubtraction { } else { if_chain! { if !expr.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TRY_FROM); + if self.msrv.meets(msrvs::TRY_FROM); if is_an_instant(cx, lhs); if is_a_duration(cx, rhs); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 601990cd6a31..7b17d8a156d5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -52,10 +52,9 @@ extern crate declare_clippy_lint; use std::io; use std::path::PathBuf; -use clippy_utils::parse_msrv; +use clippy_utils::msrvs::Msrv; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; -use rustc_semver::RustcVersion; use rustc_session::Session; #[cfg(feature = "internal")] @@ -322,48 +321,10 @@ pub use crate::utils::conf::{lookup_conf_file, Conf}; /// Used in `./src/driver.rs`. pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. + let msrv = Msrv::read(&conf.msrv, sess); + let msrv = move || msrv.clone(); - let msrv = conf.msrv.as_ref().and_then(|s| { - parse_msrv(s, None, None).or_else(|| { - sess.err(format!( - "error reading Clippy's configuration file. `{s}` is not a valid Rust version" - )); - None - }) - }); - - store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv })); -} - -fn read_msrv(conf: &Conf, sess: &Session) -> Option { - let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") - .ok() - .and_then(|v| parse_msrv(&v, None, None)); - let clippy_msrv = conf.msrv.as_ref().and_then(|s| { - parse_msrv(s, None, None).or_else(|| { - sess.err(format!( - "error reading Clippy's configuration file. `{s}` is not a valid Rust version" - )); - None - }) - }); - - if let Some(cargo_msrv) = cargo_msrv { - if let Some(clippy_msrv) = clippy_msrv { - // if both files have an msrv, let's compare them and emit a warning if they differ - if clippy_msrv != cargo_msrv { - sess.warn(format!( - "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" - )); - } - - Some(clippy_msrv) - } else { - Some(cargo_msrv) - } - } else { - clippy_msrv - } + store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() })); } #[doc(hidden)] @@ -595,43 +556,44 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); - let msrv = read_msrv(conf, sess); + let msrv = Msrv::read(&conf.msrv, sess); + let msrv = move || msrv.clone(); let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; let allow_expect_in_tests = conf.allow_expect_in_tests; let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; - store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv))); + store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, - msrv, + msrv(), allow_expect_in_tests, allow_unwrap_in_tests, )) }); - store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv))); + store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); let matches_for_let_else = conf.matches_for_let_else; - store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv))); - store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv))); - store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv))); - store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv))); - store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv))); - store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv))); - store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv))); - store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv))); - store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv(), matches_for_let_else))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv()))); + store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv()))); + store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv()))); + store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv()))); + store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv()))); + store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv()))); + store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv()))); + store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv()))); + store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv()))); store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)); - store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv))); - store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv))); + store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv()))); + store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv()))); store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length; store.register_late_pass(move |_| { Box::new(index_refutable_slice::IndexRefutableSlice::new( max_suggested_slice_pattern_length, - msrv, + msrv(), )) }); store.register_late_pass(|_| Box::::default()); @@ -648,7 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef)); store.register_late_pass(|_| Box::new(no_effect::NoEffect)); store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); - store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv))); + store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv()))); let cognitive_complexity_threshold = conf.cognitive_complexity_threshold; store.register_late_pass(move |_| { Box::new(cognitive_complexity::CognitiveComplexity::new( @@ -806,7 +768,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); - store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv))); + store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv()))); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex)); @@ -840,7 +802,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); - store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); + store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv()))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); store.register_late_pass(|_| Box::new(unused_async::UnusedAsync)); @@ -865,14 +827,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv))); + let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args; + store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields)); store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); - store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv()))); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::::default()); let allow_dbg_in_tests = conf.allow_dbg_in_tests; @@ -896,20 +859,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); - store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); + store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv()))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)); store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)); store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); - store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv))); + store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv()))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); - store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv()))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); @@ -920,7 +883,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); - store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 220941dcd5db..7cf1a6b8084a 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -3,7 +3,7 @@ use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; use rustc_hir::lang_items; @@ -481,7 +481,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { sub_visitor.visit_fn_decl(decl); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); }, - TyKind::TraitObject(bounds, ref lt, _) => { + TyKind::TraitObject(bounds, lt, _) => { if !lt.is_elided() { self.unelided_trait_object_lifetime = true; } @@ -497,14 +497,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res { self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span); } - // Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands. - // walk_generic_arg(self, generic_arg); - match generic_arg { - GenericArg::Lifetime(lt) => self.visit_lifetime(lt), - GenericArg::Type(ty) => self.visit_ty(ty), - GenericArg::Const(ct) => self.visit_anon_const(&ct.value), - GenericArg::Infer(inf) => self.visit_infer(inf), - } + walk_generic_arg(self, generic_arg); } } diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 6655c92b1da8..462d73cf0b97 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::get_parent_expr; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_parent_expr, meets_msrv, msrvs}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -34,12 +34,12 @@ declare_clippy_lint! { #[derive(Clone)] pub struct ManualBits { - msrv: Option, + msrv: Msrv, } impl ManualBits { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]); impl<'tcx> LateLintPass<'tcx> for ManualBits { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) { + if !self.msrv.meets(msrvs::MANUAL_BITS) { return; } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 02dc8755dd61..bb6d628af3b5 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -1,28 +1,25 @@ +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::higher::If; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::MaybePath; +use clippy_utils::{ + eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, +}; use itertools::Itertools; +use rustc_errors::Applicability; use rustc_errors::Diagnostic; use rustc_hir::{ def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span}; use std::ops::Deref; -use clippy_utils::{ - diagnostics::{span_lint_and_then, span_lint_hir_and_then}, - eq_expr_value, - higher::If, - is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, - sugg::Sugg, - ty::implements_trait, - visitors::is_const_evaluatable, - MaybePath, -}; -use rustc_errors::Applicability; - declare_clippy_lint! { /// ### What it does /// Identifies good opportunities for a clamp function from std or core, and suggests using it. @@ -87,11 +84,11 @@ declare_clippy_lint! { impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); pub struct ManualClamp { - msrv: Option, + msrv: Msrv, } impl ManualClamp { - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -114,7 +111,7 @@ struct InputMinMax<'tcx> { impl<'tcx> LateLintPass<'tcx> for ManualClamp { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !meets_msrv(self.msrv, msrvs::CLAMP) { + if !self.msrv.meets(msrvs::CLAMP) { return; } if !expr.span.from_expansion() { @@ -130,7 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if !meets_msrv(self.msrv, msrvs::CLAMP) { + if !self.msrv.meets(msrvs::CLAMP) { return; } for suggestion in is_two_if_pattern(cx, block) { diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index bb8c142f8e46..5ab049d8d133 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,15 +1,12 @@ +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, source::snippet}; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{def_id::DefId, sym}; -use clippy_utils::{ - diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet, -}; - declare_clippy_lint! { /// ### What it does /// Suggests to use dedicated built-in methods, @@ -45,12 +42,12 @@ declare_clippy_lint! { impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]); pub struct ManualIsAsciiCheck { - msrv: Option, + msrv: Msrv, } impl ManualIsAsciiCheck { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -70,11 +67,11 @@ enum CharRange { impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) { + if !self.msrv.meets(msrvs::IS_ASCII_DIGIT) { return; } - if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) { + if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) { return; } diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 1846596fa4c8..874d36ca9f4e 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -1,16 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; -use clippy_utils::source::snippet_opt; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::peel_blocks; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{for_each_expr, Descend}; -use clippy_utils::{meets_msrv, msrvs, peel_blocks}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -50,13 +50,13 @@ declare_clippy_lint! { } pub struct ManualLetElse { - msrv: Option, + msrv: Msrv, matches_behaviour: MatchLintBehaviour, } impl ManualLetElse { #[must_use] - pub fn new(msrv: Option, matches_behaviour: MatchLintBehaviour) -> Self { + pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self { Self { msrv, matches_behaviour, @@ -69,7 +69,7 @@ impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]); impl<'tcx> LateLintPass<'tcx> for ManualLetElse { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { let if_let_or_match = if_chain! { - if meets_msrv(self.msrv, msrvs::LET_ELSE); + if self.msrv.meets(msrvs::LET_ELSE); if !in_external_macro(cx.sess(), stmt.span); if let StmtKind::Local(local) = stmt.kind; if let Some(init) = local.init; @@ -141,20 +141,18 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: // * unused binding collision detection with existing ones // * putting patterns with at the top level | inside () // for this to be machine applicable. - let app = Applicability::HasPlaceholders; + let mut app = Applicability::HasPlaceholders; + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); + let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); + let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); - if let Some(sn_pat) = snippet_opt(cx, pat.span) && - let Some(sn_expr) = snippet_opt(cx, expr.span) && - let Some(sn_else) = snippet_opt(cx, else_body.span) - { - let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { - sn_else - } else { - format!("{{ {sn_else} }}") - }; - let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};"); - diag.span_suggestion(span, "consider writing", sugg, app); - } + let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { + sn_else.into_owned() + } else { + format!("{{ {sn_else} }}") + }; + let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};"); + diag.span_suggestion(span, "consider writing", sugg, app); }, ); } diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 4877cee0cc1e..bca193be9e71 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::is_doc_hidden; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; -use clippy_utils::{is_doc_hidden, meets_msrv, msrvs}; use rustc_ast::ast::{self, VisibilityKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -8,7 +9,6 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{self as hir, Expr, ExprKind, QPath}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::ty::DefIdTree; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; @@ -63,12 +63,12 @@ declare_clippy_lint! { #[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveStruct { - msrv: Option, + msrv: Msrv, } impl ManualNonExhaustiveStruct { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -77,14 +77,14 @@ impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); #[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveEnum { - msrv: Option, + msrv: Msrv, constructed_enum_variants: FxHashSet<(DefId, DefId)>, potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, } impl ManualNonExhaustiveEnum { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, constructed_enum_variants: FxHashSet::default(), @@ -97,7 +97,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustiveStruct { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { return; } @@ -149,7 +149,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { return; } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 6f25a2ed8e43..8d447c37150b 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,12 +1,12 @@ use clippy_utils::consts::{constant_full_int, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{in_constant, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -34,12 +34,12 @@ declare_clippy_lint! { } pub struct ManualRemEuclid { - msrv: Option, + msrv: Msrv, } impl ManualRemEuclid { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -48,11 +48,11 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]); impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::REM_EUCLID) { + if !self.msrv.meets(msrvs::REM_EUCLID) { return; } - if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) { + if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) { return; } diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index d6438ca7fec2..c1e6c82487dc 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq}; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -50,12 +50,12 @@ declare_clippy_lint! { } pub struct ManualRetain { - msrv: Option, + msrv: Msrv, } impl ManualRetain { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -71,9 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain { && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id) && match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) { - check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv); - check_iter(cx, parent_expr, left_expr, target_expr, self.msrv); - check_to_owned(cx, parent_expr, left_expr, target_expr, self.msrv); + check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); + check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); + check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv); } } @@ -85,7 +85,7 @@ fn check_into_iter( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) @@ -104,7 +104,7 @@ fn check_iter( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) @@ -127,9 +127,9 @@ fn check_to_owned( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { - if meets_msrv(msrv, msrvs::STRING_RETAIN) + if msrv.meets(msrvs::STRING_RETAIN) && let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) && match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD) @@ -215,10 +215,10 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo .any(|&method| match_def_path(cx, collect_def_id, method)) } -fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option) -> bool { +fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| { is_type_diagnostic_item(cx, expr_ty, *ty) - && acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv)) + && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv)) }) } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 0976940afac3..de166b9765f4 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,8 +1,9 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; -use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::{eq_expr_value, higher, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; @@ -11,7 +12,6 @@ use rustc_hir::BinOpKind; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; @@ -48,12 +48,12 @@ declare_clippy_lint! { } pub struct ManualStrip { - msrv: Option, + msrv: Msrv, } impl ManualStrip { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -68,7 +68,7 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { + if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) { return; } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 7d8171ead89e..7b15a307fecf 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -23,13 +23,13 @@ mod single_match; mod try_err; mod wild_in_or_pats; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_opt, walk_span_to_context}; -use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs}; +use clippy_utils::{higher, in_constant, is_span_match}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; @@ -930,13 +930,13 @@ declare_clippy_lint! { #[derive(Default)] pub struct Matches { - msrv: Option, + msrv: Msrv, infallible_destructuring_match_linted: bool, } impl Matches { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Matches::default() @@ -1000,9 +1000,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { - if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) - && match_like_matches::check_match(cx, expr, ex, arms)) - { + if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) { match_same_arms::check(cx, arms); } @@ -1034,7 +1032,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else); if !from_expansion { if let Some(else_expr) = if_let.if_else { - if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { + if self.msrv.meets(msrvs::MATCHES_MACRO) { match_like_matches::check_if_let( cx, expr, diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index c6cba81d8718..704c34c32bf7 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths}; +use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -107,7 +107,7 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); + if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()); let ready_ty = subst.type_at(0); if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); @@ -124,7 +124,7 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option< fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); + if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()); let ready_ty = subst.type_at(0); if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 0c4d9f100f7a..35024ec1224f 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res}; +use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; @@ -227,12 +227,12 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< } pub struct MemReplace { - msrv: Option, + msrv: Msrv, } impl MemReplace { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - if meets_msrv(self.msrv, msrvs::MEM_TAKE) { + if self.msrv.meets(msrvs::MEM_TAKE) { check_replace_with_default(cx, src, dest, expr.span); } } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index e9aeab2d5b62..4e6ec61f6a83 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,25 +1,25 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; -use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span}; use super::CLONED_INSTEAD_OF_COPIED; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: &Msrv) { let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let inner_ty = match recv_ty.kind() { // `Option` -> `T` ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) => + if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && msrv.meets(msrvs::OPTION_COPIED) => { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => { + _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(msrvs::ITERATOR_COPIED) => { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 720d9a68c85e..ae03da0d3f9c 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -1,27 +1,27 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::has_debug_impl; -use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span}; pub(super) fn check( cx: &LateContext<'_>, _expr: &rustc_hir::Expr<'_>, recv: &rustc_hir::Expr<'_>, - msrv: Option, expect_span: Span, err_span: Span, + msrv: &Msrv, ) { if_chain! { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); // Test the version to make sure the lint can be showed (expect_err has been // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) - if meets_msrv(msrv, msrvs::EXPECT_ERR); + if msrv.meets(msrvs::EXPECT_ERR); // Grabs the `Result` type let result_type = cx.typeck_results().expr_ty(recv); diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index ddf8a1f09b87..175e04f8ac06 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::is_trait_method; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; use rustc_span::sym; use super::FILTER_MAP_NEXT; @@ -14,10 +14,10 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { if is_trait_method(cx, expr, sym::Iterator) { - if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) { + if !msrv.meets(msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 304024e80666..301aff5ae6ac 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -1,23 +1,22 @@ //! Lint for `c.is_digit(10)` use super::IS_DIGIT_ASCII_RADIX; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::{ - consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs, - source::snippet_with_applicability, + consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability, }; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, self_arg: &'tcx Expr<'_>, radix: &'tcx Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { - if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) { + if !msrv.meets(msrvs::IS_ASCII_DIGIT) { return; } diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 6bc783c6d505..52cc1e0464bf 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; -use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks}; +use clippy_utils::{is_diag_trait_item, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -9,19 +10,12 @@ use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; -use rustc_semver::RustcVersion; use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; use super::MAP_CLONE; -pub(super) fn check( - cx: &LateContext<'_>, - e: &hir::Expr<'_>, - recv: &hir::Expr<'_>, - arg: &hir::Expr<'_>, - msrv: Option, -) { +pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) { if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id); if cx.tcx.impl_of_method(method_id) @@ -97,10 +91,10 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { ); } -fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option) { +fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) { let mut applicability = Applicability::MachineApplicable; - let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { + let (message, sugg_method) = if is_copy && msrv.meets(msrvs::ITERATOR_COPIED) { ("you are using an explicit closure for copying elements", "copied") } else { ("you are using an explicit closure for cloning elements", "cloned") diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 74fdead216b0..3122f72ee915 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::mutated_variables; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use super::MAP_UNWRAP_OR; @@ -19,13 +18,13 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, unwrap_arg: &'tcx hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) -> bool { // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) { + if is_result && !msrv.meets(msrvs::RESULT_MAP_OR_ELSE) { return false; } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 38165ab4fb26..d2913680cbb7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -104,8 +104,9 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind}; @@ -113,7 +114,6 @@ use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; @@ -3163,7 +3163,7 @@ declare_clippy_lint! { pub struct Methods { avoid_breaking_exported_api: bool, - msrv: Option, + msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, } @@ -3172,7 +3172,7 @@ impl Methods { #[must_use] pub fn new( avoid_breaking_exported_api: bool, - msrv: Option, + msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, ) -> Self { @@ -3325,7 +3325,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, receiver, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -3501,7 +3501,7 @@ impl Methods { ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv), + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv), ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { @@ -3512,7 +3512,7 @@ impl Methods { map_collect_result_unit::check(cx, expr, m_recv, m_arg); }, Some(("take", take_self_arg, [take_arg], _, _)) => { - if meets_msrv(self.msrv, msrvs::STR_REPEAT) { + if self.msrv.meets(msrvs::STR_REPEAT) { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, @@ -3539,7 +3539,7 @@ impl Methods { }, ("expect", [_]) => match method_call(recv) { Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), + Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv), _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), }, ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests), @@ -3578,7 +3578,7 @@ impl Methods { unit_hash::check(cx, expr, recv, arg); }, ("is_file", []) => filetype_is_file::check(cx, expr, recv), - ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), + ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("iter" | "iter_mut" | "into_iter", []) => { @@ -3601,7 +3601,7 @@ impl Methods { }, (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { - map_clone::check(cx, expr, recv, m_arg, self.msrv); + map_clone::check(cx, expr, recv, m_arg, &self.msrv); if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) { iter_kv_map::check(cx, map_name, expr, recv2, m_arg); } @@ -3610,8 +3610,8 @@ impl Methods { } if let Some((name, recv2, args, span2,_)) = method_call(recv) { match (name, args) { - ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), - ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), + ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv), + ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv), ("filter", [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); }, @@ -3632,7 +3632,7 @@ impl Methods { match (name2, args2) { ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), + ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv), ("iter", []) => iter_next_slice::check(cx, expr, recv2), ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), ("skip_while", [_]) => skip_while_next::check(cx, expr), @@ -3680,10 +3680,10 @@ impl Methods { vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); }, ("seek", [arg]) => { - if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) { + if self.msrv.meets(msrvs::SEEK_FROM_CURRENT) { seek_from_current::check(cx, expr, recv, arg); } - if meets_msrv(self.msrv, msrvs::SEEK_REWIND) { + if self.msrv.meets(msrvs::SEEK_REWIND) { seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span); } }, @@ -3699,7 +3699,7 @@ impl Methods { ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); - str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv); + str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { @@ -3717,7 +3717,7 @@ impl Methods { }, ("take", []) => needless_option_take::check(cx, expr, recv), ("then", [arg]) => { - if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { + if !self.msrv.meets(msrvs::BOOL_THEN_SOME) { return; } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); @@ -3760,7 +3760,7 @@ impl Methods { }, ("unwrap_or_else", [u_arg]) => match method_call(recv) { Some(("map", recv, [map_arg], _, _)) - if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, + if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index e6eb64bcbde6..3e33f9193374 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks}; +use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::sym; use super::OPTION_AS_REF_DEREF; @@ -19,9 +19,9 @@ pub(super) fn check( as_ref_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, is_mut: bool, - msrv: Option, + msrv: &Msrv, ) { - if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) { + if !msrv.meets(msrvs::OPTION_AS_DEREF) { return; } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 1acac59144ce..3c01ce1fecd3 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,9 +1,10 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; -use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; @@ -12,7 +13,6 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span, Symbol, SyntaxContext}; use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; @@ -24,7 +24,7 @@ pub(super) fn check( self_arg: &Expr<'_>, pat_arg: &Expr<'_>, count: u128, - msrv: Option, + msrv: &Msrv, ) { if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; @@ -34,7 +34,7 @@ pub(super) fn check( IterUsageKind::Nth(n) => count > n + 1, IterUsageKind::NextTuple => count > 2, }; - let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE); + let manual = count == 2 && msrv.meets(msrvs::STR_SPLIT_ONCE); match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 7ff13b95626b..17b0507682ae 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -1,13 +1,11 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty, -}; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node}; use rustc_hir_typeck::{FnCtxt, Inherited}; @@ -16,14 +14,9 @@ use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; -use rustc_middle::ty::EarlyBinder; -use rustc_middle::ty::{self, Clause, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; -use rustc_semver::RustcVersion; +use rustc_middle::ty::{self, Clause, EarlyBinder, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; use rustc_span::{sym, Symbol}; -use rustc_trait_selection::traits::{ - query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause, -}; -use std::cmp::max; +use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; use super::UNNECESSARY_TO_OWNED; @@ -33,7 +26,7 @@ pub fn check<'tcx>( method_name: Symbol, receiver: &'tcx Expr<'_>, args: &'tcx [Expr<'_>], - msrv: Option, + msrv: &Msrv, ) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); @@ -204,7 +197,7 @@ fn check_into_iter_call_arg( expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>, - msrv: Option, + msrv: &Msrv, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -219,7 +212,7 @@ fn check_into_iter_call_arg( if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { + let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" @@ -267,11 +260,22 @@ fn check_other_call_arg<'tcx>( if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id; let receiver_ty = cx.typeck_results().expr_ty(receiver); - if can_change_type(cx, maybe_arg, receiver_ty); // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match // `Target = T`. - if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id; - let n_refs = max(n_refs, usize::from(!is_copy(cx, receiver_ty))); + if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) { + Some((n_refs, receiver_ty)) + } else if trait_predicate.def_id() != deref_trait_id { + Some((1, cx.tcx.mk_ref( + cx.tcx.lifetimes.re_erased, + ty::TypeAndMut { + ty: receiver_ty, + mutbl: Mutability::Not, + }, + ))) + } else { + None + }; + if can_change_type(cx, maybe_arg, receiver_ty); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { span_lint_and_sugg( @@ -345,13 +349,13 @@ fn get_input_traits_and_projections<'tcx>( if trait_predicate.trait_ref.self_ty() == input { trait_predicates.push(trait_predicate); } - } + }, PredicateKind::Clause(Clause::Projection(projection_predicate)) => { if projection_predicate.projection_ty.self_ty() == input { projection_predicates.push(projection_predicate); } - } - _ => {} + }, + _ => {}, } } (trait_predicates, projection_predicates) @@ -403,10 +407,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< let mut trait_predicates = cx.tcx.param_env(callee_def_id) .caller_bounds().iter().filter(|predicate| { - if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() - && trait_predicate.trait_ref.self_ty() == *param_ty { - true - } else { + if let PredicateKind::Clause(Clause::Trait(trait_predicate)) + = predicate.kind().skip_binder() + && trait_predicate.trait_ref.self_ty() == *param_ty + { + true + } else { false } }); @@ -466,12 +472,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. -fn is_to_owned_like<'a>( - cx: &LateContext<'a>, - call_expr: &Expr<'a>, - method_name: Symbol, - method_def_id: DefId, -) -> bool { +fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { is_clone_like(cx, method_name.as_str(), method_def_id) || is_cow_into_owned(cx, method_name, method_def_id) || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 71cc0d0a81cd..5bc04bc17fb4 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::ty::has_drop; -use clippy_utils::{ - fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method, -}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; @@ -11,7 +10,6 @@ use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -75,12 +73,12 @@ declare_clippy_lint! { impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); pub struct MissingConstForFn { - msrv: Option, + msrv: Msrv, } impl MissingConstForFn { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -95,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { - if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) { + if !self.msrv.meets(msrvs::CONST_IF_MATCH) { return; } @@ -152,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { cx.tcx.sess.span_err(span, err.as_ref()); } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 75e12715458f..2f0b7ce16e51 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{ + implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item, +}; use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -124,7 +126,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { .filter_map(|obligation| { // Note that we do not want to deal with qualified predicates here. match obligation.predicate.kind().no_bound_vars() { - Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => Some(pred), + Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => { + Some(pred) + }, _ => None, } }) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 819646bb6780..79c1ae4861e8 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -6,7 +6,8 @@ use clippy_utils::ty::has_drop; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::ops::Deref; @@ -159,8 +160,11 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if_chain! { if let StmtKind::Semi(expr) = stmt.kind; + let ctxt = stmt.span.ctxt(); + if expr.span.ctxt() == ctxt; if let Some(reduced) = reduce_expression(cx, expr); - if !&reduced.iter().any(|e| e.span.from_expansion()); + if !in_external_macro(cx.sess(), stmt.span); + if reduced.iter().all(|e| e.span.ctxt() == ctxt); then { if let ExprKind::Index(..) = &expr.kind { let snippet = if let (Some(arr), Some(func)) = diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 92920bbad6e0..e395ff54cb15 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -490,7 +490,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( ty_name: name.ident.name, method_renames, ref_prefix: RefPrefix { - lt: lt.clone(), + lt: *lt, mutability, }, deref_ty, @@ -693,9 +693,10 @@ fn matches_preds<'tcx>( cx.tcx, ObligationCause::dummy(), cx.param_env, - cx.tcx.mk_predicate(Binder::dummy( - PredicateKind::Clause(Clause::Projection(p.with_self_ty(cx.tcx, ty))), - )), + cx.tcx + .mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Projection( + p.with_self_ty(cx.tcx, ty), + )))), )), ExistentialPredicate::AutoTrait(p) => infcx .type_implements_trait(p, [ty], cx.param_env) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index c6fbb5e805ab..0a1b9d173cf9 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,16 +1,16 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, path_to_local}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use std::cmp::Ordering; @@ -161,12 +161,12 @@ declare_clippy_lint! { } pub struct Ranges { - msrv: Option, + msrv: Msrv, } impl Ranges { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -181,7 +181,7 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, l, r) = expr.kind { - if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { + if self.msrv.meets(msrvs::RANGE_CONTAINS) { check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 8e675d34a183..2a42e73488f1 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -81,8 +81,8 @@ impl EarlyLintPass for RedundantClosureCall { "try not to call a closure in the expression where it is declared", |diag| { if fn_decl.inputs.is_empty() { - let app = Applicability::MachineApplicable; - let mut hint = Sugg::ast(cx, body, ".."); + let mut app = Applicability::MachineApplicable; + let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app); if asyncness.is_async() { // `async x` is a syntax error, so it becomes `async { x }` diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 40b03068f6c7..61bff4a0e38d 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -37,12 +36,12 @@ declare_clippy_lint! { } pub struct RedundantFieldNames { - msrv: Option, + msrv: Msrv, } impl RedundantFieldNames { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -51,7 +50,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) { + if !self.msrv.meets(msrvs::FIELD_INIT_SHORTHAND) { return; } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 60ba62c4a433..3aa2490bc44e 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; -use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -34,12 +33,12 @@ declare_clippy_lint! { } pub struct RedundantStaticLifetimes { - msrv: Option, + msrv: Msrv, } impl RedundantStaticLifetimes { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -96,7 +95,7 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) { + if !self.msrv.meets(msrvs::STATIC_IN_CONST) { return; } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2b2a41d16011..81143d7799ea 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -12,6 +12,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::{BytePos, Pos}; declare_clippy_lint! { /// ### What it does @@ -209,13 +210,14 @@ fn check_final_expr<'tcx>( if cx.tcx.hir().attrs(expr.hir_id).is_empty() { let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); if !borrows { - emit_return_lint( - cx, - peeled_drop_expr.span, - semi_spans, - inner.as_ref().map(|i| i.span), - replacement, - ); + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement); } } }, @@ -289,3 +291,16 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) }) .is_some() } + +// Go backwards while encountering whitespace and extend the given Span to that point. +fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { + if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { + let ws = [' ', '\t', '\n']; + if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) { + let len = prev_source.len() - non_ws_pos - 1; + return sp.with_lo(sp.lo() - BytePos::from_usize(len)); + } + } + + sp +} diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 424a6e9264e4..83e651aba8e8 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -16,10 +16,10 @@ mod utils; mod wrong_transmute; use clippy_utils::in_constant; +use clippy_utils::msrvs::Msrv; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; @@ -410,7 +410,7 @@ declare_clippy_lint! { } pub struct Transmute { - msrv: Option, + msrv: Msrv, } impl_lint_pass!(Transmute => [ CROSSPOINTER_TRANSMUTE, @@ -431,7 +431,7 @@ impl_lint_pass!(Transmute => [ ]); impl Transmute { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -461,7 +461,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmuting_null::check(cx, e, arg, to_ty) - | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) + | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 12d0b866e1c9..3dde4eee6717 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -1,12 +1,12 @@ use super::TRANSMUTE_PTR_TO_REF; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{meets_msrv, msrvs, sugg}; +use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitable}; -use rustc_semver::RustcVersion; /// Checks for `transmute_ptr_to_ref` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>( to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, path: &'tcx Path<'_>, - msrv: Option, + msrv: &Msrv, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => { @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); - if meets_msrv(msrv, msrvs::POINTER_CAST) { + if msrv.meets(msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() @@ -46,7 +46,7 @@ pub(super) fn check<'tcx>( } } else if from_ptr_ty.ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { - if meets_msrv(msrv, msrvs::POINTER_CAST) { + if msrv.meets(msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index e8f15a444735..2e1b6d8d4ea7 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,6 +1,10 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::walk_span_to_context; +use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{get_parent_node, is_lint_allowed}; +use hir::HirId; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; @@ -59,11 +63,39 @@ declare_clippy_lint! { restriction, "creating an unsafe block without explaining why it is safe" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `// SAFETY: ` comments on safe code. + /// + /// ### Why is this bad? + /// Safe code has no safety requirements, so there is no need to + /// describe safety invariants. + /// + /// ### Example + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// // SAFETY: references are guaranteed to be non-null. + /// let ptr = NonNull::new(a).unwrap(); + /// ``` + /// Use instead: + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// let ptr = NonNull::new(a).unwrap(); + /// ``` + #[clippy::version = "1.67.0"] + pub UNNECESSARY_SAFETY_COMMENT, + restriction, + "annotating safe code with a safety comment" +} -declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); +declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]); -impl LateLintPass<'_> for UndocumentedUnsafeBlocks { - fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) { +impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) && !in_external_macro(cx.tcx.sess, block.span) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) @@ -87,35 +119,175 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { "consider adding a safety comment on the preceding line", ); } + + if let Some(tail) = block.expr + && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) + && !in_external_macro(cx.tcx.sess, tail.span) + && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id) + && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) + { + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + tail.span, + "expression has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } } - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if let hir::ItemKind::Impl(imple) = item.kind - && imple.unsafety == hir::Unsafety::Unsafe - && !in_external_macro(cx.tcx.sess, item.span) - && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) - && !is_unsafe_from_proc_macro(cx, item.span) - && !item_has_safety_comment(cx, item) + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) { + let ( + hir::StmtKind::Local(&hir::Local { init: Some(expr), .. }) + | hir::StmtKind::Expr(expr) + | hir::StmtKind::Semi(expr) + ) = stmt.kind else { return }; + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) + && !in_external_macro(cx.tcx.sess, stmt.span) + && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) + && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + stmt.span, + "statement has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } + } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } + + let mk_spans = |pos: BytePos| { let source_map = cx.tcx.sess.source_map(); + let span = Span::new(pos, pos, SyntaxContext::root(), None); + let help_span = source_map.span_extend_to_next_char(span, '\n', true); let span = if source_map.is_multiline(item.span) { source_map.span_until_char(item.span, '\n') } else { item.span }; + (span, help_span) + }; - span_lint_and_help( - cx, - UNDOCUMENTED_UNSAFE_BLOCKS, - span, - "unsafe impl missing a safety comment", - None, - "consider adding a safety comment on the preceding line", - ); + let item_has_safety_comment = item_has_safety_comment(cx, item); + match (&item.kind, item_has_safety_comment) { + // lint unsafe impl without safety comment + (hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => { + if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) + && !is_unsafe_from_proc_macro(cx, item.span) + { + let source_map = cx.tcx.sess.source_map(); + let span = if source_map.is_multiline(item.span) { + source_map.span_until_char(item.span, '\n') + } else { + item.span + }; + + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe impl missing a safety comment", + None, + "consider adding a safety comment on the preceding line", + ); + } + }, + // lint safe impl with unnecessary safety comment + (hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + "impl has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } + }, + (hir::ItemKind::Impl(_), _) => {}, + // const and static items only need a safety comment if their body is an unsafe block, lint otherwise + (&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) { + let body = cx.tcx.hir().body(body); + if !matches!( + body.value.kind, hir::ExprKind::Block(block, _) + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + ) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + &format!("{} has unnecessary safety comment", item.kind.descr()), + Some(help_span), + "consider removing the safety comment", + ); + } + } + }, + // Aside from unsafe impls and consts/statics with an unsafe block, items in general + // do not have safety invariants that need to be documented, so lint those. + (_, HasSafetyComment::Yes(pos)) => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + &format!("{} has unnecessary safety comment", item.kind.descr()), + Some(help_span), + "consider removing the safety comment", + ); + } + }, + _ => (), } } } +fn expr_has_unnecessary_safety_comment<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + comment_pos: BytePos, +) -> Option { + // this should roughly be the reverse of `block_parents_have_safety_comment` + if for_each_expr_with_closures(cx, expr, |expr| match expr.kind { + hir::ExprKind::Block( + Block { + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }, + _, + ) => ControlFlow::Break(()), + // statements will be handled by check_stmt itself again + hir::ExprKind::Block(..) => ControlFlow::Continue(Descend::No), + _ => ControlFlow::Continue(Descend::Yes), + }) + .is_some() + { + return None; + } + + let source_map = cx.tcx.sess.source_map(); + let span = Span::new(comment_pos, comment_pos, SyntaxContext::root(), None); + let help_span = source_map.span_extend_to_next_char(span, '\n', true); + + Some(help_span) +} + fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); let file_pos = source_map.lookup_byte_offset(span.lo()); @@ -170,85 +342,134 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // won't work. This is to avoid dealing with where such a comment should be place relative to // attributes and doc comments. - span_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span) + matches!( + span_from_macro_expansion_has_safety_comment(cx, span), + HasSafetyComment::Yes(_) + ) || span_in_body_has_safety_comment(cx, span) +} + +enum HasSafetyComment { + Yes(BytePos), + No, + Maybe, } /// Checks if the lines immediately preceding the item contain a safety comment. #[allow(clippy::collapsible_match)] -fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { - if span_from_macro_expansion_has_safety_comment(cx, item.span) { - return true; +fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, item.span) { + HasSafetyComment::Maybe => (), + has_safety_comment => return has_safety_comment, } - if item.span.ctxt() == SyntaxContext::root() { - if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { - let comment_start = match parent_node { - Node::Crate(parent_mod) => { - comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) - }, - Node::Item(parent_item) => { - if let ItemKind::Mod(parent_mod) = &parent_item.kind { - comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item) - } else { - // Doesn't support impls in this position. Pretend a comment was found. - return true; - } - }, - Node::Stmt(stmt) => { - if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) { - match stmt_parent { - Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), - _ => { - // Doesn't support impls in this position. Pretend a comment was found. - return true; - }, - } - } else { - // Problem getting the parent node. Pretend a comment was found. - return true; - } - }, - _ => { + if item.span.ctxt() != SyntaxContext::root() { + return HasSafetyComment::No; + } + if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { + let comment_start = match parent_node { + Node::Crate(parent_mod) => { + comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) + }, + Node::Item(parent_item) => { + if let ItemKind::Mod(parent_mod) = &parent_item.kind { + comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item) + } else { // Doesn't support impls in this position. Pretend a comment was found. - return true; - }, - }; + return HasSafetyComment::Maybe; + } + }, + Node::Stmt(stmt) => { + if let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) { + walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo) + } else { + // Problem getting the parent node. Pretend a comment was found. + return HasSafetyComment::Maybe; + } + }, + _ => { + // Doesn't support impls in this position. Pretend a comment was found. + return HasSafetyComment::Maybe; + }, + }; - let source_map = cx.sess().source_map(); - if let Some(comment_start) = comment_start - && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) - && let Ok(comment_start_line) = source_map.lookup_line(comment_start) - && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) - && let Some(src) = unsafe_line.sf.src.as_deref() - { - unsafe_line.sf.lines(|lines| { - comment_start_line.line < unsafe_line.line && text_has_safety_comment( + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + return unsafe_line.sf.lines(|lines| { + if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( src, &lines[comment_start_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos.to_usize(), - ) - }) - } else { - // Problem getting source text. Pretend a comment was found. - true - } - } else { - // No parent node. Pretend a comment was found. - true + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } + }); + } + } + HasSafetyComment::Maybe +} + +/// Checks if the lines immediately preceding the item contain a safety comment. +#[allow(clippy::collapsible_match)] +fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, span) { + HasSafetyComment::Maybe => (), + has_safety_comment => return has_safety_comment, + } + + if span.ctxt() != SyntaxContext::root() { + return HasSafetyComment::No; + } + + if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) { + let comment_start = match parent_node { + Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), + _ => return HasSafetyComment::Maybe, + }; + + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + return unsafe_line.sf.lines(|lines| { + if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &lines[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } + }); } - } else { - false } + HasSafetyComment::Maybe } -fn comment_start_before_impl_in_mod( +fn comment_start_before_item_in_mod( cx: &LateContext<'_>, parent_mod: &hir::Mod<'_>, parent_mod_span: Span, - imple: &hir::Item<'_>, + item: &hir::Item<'_>, ) -> Option { parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| { - if *item_id == imple.item_id() { + if *item_id == item.item_id() { if idx == 0 { // mod A { /* comment */ unsafe impl T {} ... } // ^------------------------------------------^ returns the start of this span @@ -270,11 +491,11 @@ fn comment_start_before_impl_in_mod( }) } -fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); if ctxt == SyntaxContext::root() { - false + HasSafetyComment::Maybe } else { // From a macro expansion. Get the text from the start of the macro declaration to start of the // unsafe block. @@ -286,15 +507,22 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && let Some(src) = unsafe_line.sf.src.as_deref() { unsafe_line.sf.lines(|lines| { - macro_line.line < unsafe_line.line && text_has_safety_comment( - src, - &lines[macro_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + if macro_line.line < unsafe_line.line { + match text_has_safety_comment( + src, + &lines[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } else { + HasSafetyComment::No + } }) } else { // Problem getting source text. Pretend a comment was found. - true + HasSafetyComment::Maybe } } } @@ -333,7 +561,7 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { src, &lines[body_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos.to_usize(), - ) + ).is_some() }) } else { // Problem getting source text. Pretend a comment was found. @@ -345,30 +573,34 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { } /// Checks if the given text has a safety comment for the immediately proceeding line. -fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool { +fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> Option { let mut lines = line_starts .array_windows::<2>() .rev() .map_while(|[start, end]| { let start = start.to_usize() - offset; let end = end.to_usize() - offset; - src.get(start..end).map(|text| (start, text.trim_start())) + let text = src.get(start..end)?; + let trimmed = text.trim_start(); + Some((start + (text.len() - trimmed.len()), trimmed)) }) .filter(|(_, text)| !text.is_empty()); let Some((line_start, line)) = lines.next() else { - return false; + return None; }; // Check for a sequence of line comments. if line.starts_with("//") { - let mut line = line; + let (mut line, mut line_start) = (line, line_start); loop { if line.to_ascii_uppercase().contains("SAFETY:") { - return true; + return Some(BytePos( + u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(), + )); } match lines.next() { - Some((_, x)) if x.starts_with("//") => line = x, - _ => return false, + Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s), + _ => return None, } } } @@ -377,16 +609,19 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> let (mut line_start, mut line) = (line_start, line); loop { if line.starts_with("/*") { - let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start(); + let src = &src[line_start..line_starts.last().unwrap().to_usize() - offset]; let mut tokens = tokenize(src); - return src[..tokens.next().unwrap().len as usize] + return (src[..tokens.next().unwrap().len as usize] .to_ascii_uppercase() .contains("SAFETY:") - && tokens.all(|t| t.kind == TokenKind::Whitespace); + && tokens.all(|t| t.kind == TokenKind::Whitespace)) + .then_some(BytePos( + u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(), + )); } match lines.next() { Some(x) => (line_start, line) = x, - None => return false, + None => return None, } } } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index bb6fb38e9690..7355260ae4af 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -2,14 +2,14 @@ use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{meets_msrv, msrvs, over}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::over; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::DUMMY_SP; @@ -45,14 +45,13 @@ declare_clippy_lint! { "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" } -#[derive(Clone, Copy)] pub struct UnnestedOrPatterns { - msrv: Option, + msrv: Msrv, } impl UnnestedOrPatterns { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -61,13 +60,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &a.pat); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { if let ast::ExprKind::Let(pat, _, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } @@ -75,13 +74,13 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &p.pat); } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &l.pat); } } diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index aac6719a8dc0..097568cd1f70 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -29,22 +30,16 @@ declare_clippy_lint! { } declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); -fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { +fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> { if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") && let ExprKind::Lit(token_lit) = &receiver.kind - && token_lit.is_semantic_float() { - let mut f_str = token_lit.symbol.to_string(); - let f = f_str.trim_end_matches('_').parse::().unwrap(); - if let Some(suffix) = token_lit.suffix { - f_str.push_str(suffix.as_str()); - } - if f.fract() == 0.0 { - Some((method_name, f_str)) - } else { - None - } + && token_lit.is_semantic_float() + && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::() { + (f.fract() == 0.0).then(|| + (method_name, snippet(cx, receiver.span, "..").to_string()) + ) } else { None } @@ -52,7 +47,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { impl EarlyLintPass for UnusedRounding { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if let Some((method_name, float)) = is_useless_rounding(expr) { + if let Some((method_name, float)) = is_useless_rounding(cx, expr) { span_lint_and_sugg( cx, UNUSED_ROUNDING, diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index e2860db71a5a..4c755d812a0e 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::same_type_and_consts; -use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -14,7 +15,6 @@ use rustc_hir::{ }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -57,13 +57,13 @@ declare_clippy_lint! { #[derive(Default)] pub struct UseSelf { - msrv: Option, + msrv: Msrv, stack: Vec, } impl UseSelf { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Self::default() @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { if_chain! { if !hir_ty.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, in_body, @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if !expr.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id); then {} else { return; } @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { if_chain! { if !pat.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); // get the path from the pattern if let PatKind::Path(QPath::Resolved(_, path)) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b37d4239477e..b6dc8cd7ab11 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -402,6 +402,10 @@ define_Conf! { /// A list of paths to types that should be treated like `Arc`, i.e. ignored but /// for the generic parameters for determining interior mutability (ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()])), + /// Lint: UNINLINED_FORMAT_ARGS. + /// + /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` + (allow_mixed_uninlined_format_args: bool = true), } /// Search for the configuration file. diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index 1e994e3f2b17..9876a8a765cc 100644 --- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -41,7 +41,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .type_of(f.did) .walk() .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) + .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV)) }); if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); then { diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index cd8575c90e86..7987a233bdc1 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -125,19 +125,19 @@ fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &' } } -pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option { - let mut unique_attr = None; +pub fn get_unique_attr<'a>( + sess: &'a Session, + attrs: &'a [ast::Attribute], + name: &'static str, +) -> Option<&'a ast::Attribute> { + let mut unique_attr: Option<&ast::Attribute> = None; for attr in get_attr(sess, attrs, name) { - match attr.style { - ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), - ast::AttrStyle::Inner => { - sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times")) - .span_note(unique_attr.as_ref().unwrap().span, "first definition found here") - .emit(); - }, - ast::AttrStyle::Outer => { - sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute")); - }, + if let Some(duplicate) = unique_attr { + sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times")) + .span_note(duplicate.span, "first definition found here") + .emit(); + } else { + unique_attr = Some(attr); } } unique_attr diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index f74f7dadfa90..96711936968b 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -91,6 +91,16 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: } } +fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res { + cx.typeck_results() + .expr_ty(e) + .has_significant_drop(cx.tcx, cx.param_env) + } else { + false + } +} + #[expect(clippy::too_many_lines)] fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { struct V<'cx, 'tcx> { @@ -113,13 +123,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, args, ) => match self.cx.qpath_res(path, hir_id) { - Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => { - if self - .cx - .typeck_results() - .expr_ty(e) - .has_significant_drop(self.cx.tcx, self.cx.param_env) - { + res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => { + if res_has_significant_drop(res, self.cx, e) { self.eagerness = ForceNoChange; return; } @@ -147,6 +152,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS self.eagerness |= NoChange; return; }, + ExprKind::Path(ref path) => { + if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + self.eagerness = ForceNoChange; + return; + } + }, ExprKind::MethodCall(name, ..) => { self.eagerness |= self .cx @@ -206,7 +217,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS | ExprKind::Match(..) | ExprKind::Closure { .. } | ExprKind::Field(..) - | ExprKind::Path(_) | ExprKind::AddrOf(..) | ExprKind::Struct(..) | ExprKind::Repeat(..) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9e2682925a22..90192f46cbfa 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -105,8 +105,6 @@ use rustc_middle::ty::{ layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture, }; use rustc_middle::ty::{FloatTy, IntTy, UintTy}; -use rustc_semver::RustcVersion; -use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::sym; @@ -118,36 +116,17 @@ use crate::consts::{constant, Constant}; use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param}; use crate::visitors::for_each_expr; -pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { - if let Ok(version) = RustcVersion::parse(msrv) { - return Some(version); - } else if let Some(sess) = sess { - if let Some(span) = span { - sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); - } - } - None -} - -pub fn meets_msrv(msrv: Option, lint_msrv: RustcVersion) -> bool { - msrv.map_or(true, |msrv| msrv.meets(lint_msrv)) -} - #[macro_export] macro_rules! extract_msrv_attr { ($context:ident) => { fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { let sess = rustc_lint::LintContext::sess(cx); - match $crate::get_unique_inner_attr(sess, attrs, "msrv") { - Some(msrv_attr) => { - if let Some(msrv) = msrv_attr.value_str() { - self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span)); - } else { - sess.span_err(msrv_attr.span, "bad clippy attribute"); - } - }, - _ => (), - } + self.msrv.enter_lint_attrs(sess, attrs); + } + + fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { + let sess = rustc_lint::LintContext::sess(cx); + self.msrv.exit_lint_attrs(sess, attrs); } }; } diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 79b19e6fb3eb..12a512f78a69 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -1,4 +1,11 @@ +use std::sync::OnceLock; + +use rustc_ast::Attribute; use rustc_semver::RustcVersion; +use rustc_session::Session; +use rustc_span::Span; + +use crate::attrs::get_unique_attr; macro_rules! msrv_aliases { ($($major:literal,$minor:literal,$patch:literal { @@ -40,3 +47,97 @@ msrv_aliases! { 1,16,0 { STR_REPEAT } 1,55,0 { SEEK_REWIND } } + +fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { + if let Ok(version) = RustcVersion::parse(msrv) { + return Some(version); + } else if let Some(sess) = sess { + if let Some(span) = span { + sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); + } + } + None +} + +/// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]` +#[derive(Debug, Clone, Default)] +pub struct Msrv { + stack: Vec, +} + +impl Msrv { + fn new(initial: Option) -> Self { + Self { + stack: Vec::from_iter(initial), + } + } + + fn read_inner(conf_msrv: &Option, sess: &Session) -> Self { + let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") + .ok() + .and_then(|v| parse_msrv(&v, None, None)); + let clippy_msrv = conf_msrv.as_ref().and_then(|s| { + parse_msrv(s, None, None).or_else(|| { + sess.err(format!( + "error reading Clippy's configuration file. `{s}` is not a valid Rust version" + )); + None + }) + }); + + // if both files have an msrv, let's compare them and emit a warning if they differ + if let Some(cargo_msrv) = cargo_msrv + && let Some(clippy_msrv) = clippy_msrv + && clippy_msrv != cargo_msrv + { + sess.warn(format!( + "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" + )); + } + + Self::new(clippy_msrv.or(cargo_msrv)) + } + + /// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version` + /// field in `Cargo.toml` + /// + /// Returns a `&'static Msrv` as `Copy` types are more easily passed to the + /// `register_{late,early}_pass` callbacks + pub fn read(conf_msrv: &Option, sess: &Session) -> &'static Self { + static PARSED: OnceLock = OnceLock::new(); + + PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess)) + } + + pub fn current(&self) -> Option { + self.stack.last().copied() + } + + pub fn meets(&self, required: RustcVersion) -> bool { + self.current().map_or(true, |version| version.meets(required)) + } + + fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option { + if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") { + if let Some(msrv) = msrv_attr.value_str() { + return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span)); + } + + sess.span_err(msrv_attr.span, "bad clippy attribute"); + } + + None + } + + pub fn enter_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) { + if let Some(version) = Self::parse_attr(sess, attrs) { + self.stack.push(version); + } + } + + pub fn exit_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) { + if Self::parse_attr(sess, attrs).is_some() { + self.stack.pop(); + } + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 6c09c146082a..6417f0f3c713 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -60,6 +60,8 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; #[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"]; +#[cfg(feature = "internal")] +pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; @@ -72,7 +74,6 @@ pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekab pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; #[cfg_attr(not(unix), allow(clippy::invalid_paths))] pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"]; -pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"]; pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; @@ -101,8 +102,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; -#[cfg(feature = "internal")] -pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index b8c2dd5ab9ea..480e8e55cf39 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -3,6 +3,7 @@ // of terminologies might not be relevant in the context of Clippy. Note that its behavior might // differ from the time of `rustc` even if the name stays the same. +use crate::msrvs::Msrv; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::{ @@ -18,20 +19,22 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option) -> McfResult { +pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { let predicates = tcx.predicates_of(current); for (predicate, _) in predicates.predicates { match predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_)) - | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_)) + ty::PredicateKind::Clause( + ty::Clause::RegionOutlives(_) + | ty::Clause::TypeOutlives(_) + | ty::Clause::Projection(_) + | ty::Clause::Trait(..), + ) | ty::PredicateKind::WellFormed(_) - | ty::PredicateKind::Clause(ty::Clause::Projection(_)) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::Clause(ty::Clause::Trait(..)) | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"), @@ -281,7 +284,7 @@ fn check_terminator<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, terminator: &Terminator<'tcx>, - msrv: Option, + msrv: &Msrv, ) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { @@ -365,7 +368,7 @@ fn check_terminator<'tcx>( } } -fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bool { +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { tcx.is_const_fn(def_id) && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level { @@ -384,15 +387,12 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bo let since = rustc_span::Symbol::intern(short_version); - crate::meets_msrv( - msrv, - RustcVersion::parse(since.as_str()).unwrap_or_else(|err| { - panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}") - }), - ) + msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| { + panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}") + })) } else { // Unstable const fn with the feature enabled. - msrv.is_none() + msrv.current().is_none() } }) } diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index eacfa91ba556..cd5dcfdaca34 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; +use rustc_session::Session; use rustc_span::hygiene; use rustc_span::source_map::{original_sp, SourceMap}; use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; @@ -204,11 +205,20 @@ pub fn snippet_with_applicability<'a, T: LintContext>( span: Span, default: &'a str, applicability: &mut Applicability, +) -> Cow<'a, str> { + snippet_with_applicability_sess(cx.sess(), span, default, applicability) +} + +fn snippet_with_applicability_sess<'a>( + sess: &Session, + span: Span, + default: &'a str, + applicability: &mut Applicability, ) -> Cow<'a, str> { if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - snippet_opt(cx, span).map_or_else( + snippet_opt_sess(sess, span).map_or_else( || { if *applicability == Applicability::MachineApplicable { *applicability = Applicability::HasPlaceholders; @@ -226,8 +236,12 @@ pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, defau } /// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(cx: &T, span: Span) -> Option { - cx.sess().source_map().span_to_snippet(span).ok() +pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option { + snippet_opt_sess(cx.sess(), span) +} + +fn snippet_opt_sess(sess: &Session, span: Span) -> Option { + sess.source_map().span_to_snippet(span).ok() } /// Converts a span (from a block) to a code snippet if available, otherwise use default. @@ -277,8 +291,8 @@ pub fn snippet_block<'a, T: LintContext>( /// Same as `snippet_block`, but adapts the applicability level by the rules of /// `snippet_with_applicability`. -pub fn snippet_block_with_applicability<'a, T: LintContext>( - cx: &T, +pub fn snippet_block_with_applicability<'a>( + cx: &impl LintContext, span: Span, default: &'a str, indent_relative_to: Option, @@ -299,7 +313,17 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( /// /// This will also return whether or not the snippet is a macro call. pub fn snippet_with_context<'a>( - cx: &LateContext<'_>, + cx: &impl LintContext, + span: Span, + outer: SyntaxContext, + default: &'a str, + applicability: &mut Applicability, +) -> (Cow<'a, str>, bool) { + snippet_with_context_sess(cx.sess(), span, outer, default, applicability) +} + +fn snippet_with_context_sess<'a>( + sess: &Session, span: Span, outer: SyntaxContext, default: &'a str, @@ -318,7 +342,7 @@ pub fn snippet_with_context<'a>( ); ( - snippet_with_applicability(cx, span, default, applicability), + snippet_with_applicability_sess(sess, span, default, applicability), is_macro_call, ) } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3cacdb493772..b66604f33db1 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -176,25 +176,28 @@ impl<'a> Sugg<'a> { } /// Prepare a suggestion from an expression. - pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { + pub fn ast( + cx: &EarlyContext<'_>, + expr: &ast::Expr, + default: &'a str, + ctxt: SyntaxContext, + app: &mut Applicability, + ) -> Self { use rustc_ast::ast::RangeLimits; - let snippet_without_expansion = |cx, span: Span, default| { - if span.from_expansion() { - snippet_with_macro_callsite(cx, span, default) - } else { - snippet(cx, span, default) - } - }; - + #[expect(clippy::match_wildcard_for_single_variants)] match expr.kind { + _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::AddrOf(..) | ast::ExprKind::Box(..) | ast::ExprKind::Closure { .. } | ast::ExprKind::If(..) | ast::ExprKind::Let(..) | ast::ExprKind::Unary(..) - | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)), + | ast::ExprKind::Match(..) => match snippet_with_context(cx, expr.span, ctxt, default, app) { + (snip, false) => Sugg::MaybeParen(snip), + (snip, true) => Sugg::NonParen(snip), + }, ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Break(..) @@ -224,45 +227,49 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Array(..) | ast::ExprKind::While(..) | ast::ExprKind::Await(..) - | ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)), + | ast::ExprKind::Err => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( AssocOp::DotDot, - lhs.as_ref() - .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)), - rhs.as_ref() - .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)), + lhs.as_ref().map_or("".into(), |lhs| { + snippet_with_context(cx, lhs.span, ctxt, default, app).0 + }), + rhs.as_ref().map_or("".into(), |rhs| { + snippet_with_context(cx, rhs.span, ctxt, default, app).0 + }), ), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp( AssocOp::DotDotEq, - lhs.as_ref() - .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)), - rhs.as_ref() - .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)), + lhs.as_ref().map_or("".into(), |lhs| { + snippet_with_context(cx, lhs.span, ctxt, default, app).0 + }), + rhs.as_ref().map_or("".into(), |rhs| { + snippet_with_context(cx, rhs.span, ctxt, default, app).0 + }), ), ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp( AssocOp::Assign, - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, rhs.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, rhs.span, ctxt, default, app).0, ), ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp( astbinop2assignop(op), - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, rhs.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, rhs.span, ctxt, default, app).0, ), ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( AssocOp::from_ast_binop(op.node), - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, rhs.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, rhs.span, ctxt, default, app).0, ), ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp( AssocOp::As, - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, ty.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, ty.span, ctxt, default, app).0, ), ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp( AssocOp::Colon, - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, ty.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, ty.span, ctxt, default, app).0, ), } } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 2ceda3511fe4..bfb2d472a393 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -9,7 +9,10 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; -use rustc_infer::infer::{TyCtxtInferExt, type_variable::{TypeVariableOrigin, TypeVariableOriginKind}}; +use rustc_infer::infer::{ + type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, + TyCtxtInferExt, +}; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ @@ -189,7 +192,13 @@ pub fn implements_trait<'tcx>( trait_id: DefId, ty_params: &[GenericArg<'tcx>], ) -> bool { - implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params.iter().map(|&arg| Some(arg))) + implements_trait_with_env( + cx.tcx, + cx.param_env, + ty, + trait_id, + ty_params.iter().map(|&arg| Some(arg)), + ) } /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. @@ -212,7 +221,11 @@ pub fn implements_trait_with_env<'tcx>( kind: TypeVariableOriginKind::MiscVariable, span: DUMMY_SP, }; - let ty_params = tcx.mk_substs(ty_params.into_iter().map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into()))); + let ty_params = tcx.mk_substs( + ty_params + .into_iter() + .map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())), + ); infcx .type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env) .must_apply_modulo_regions() @@ -712,7 +725,9 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O } inputs = Some(i); }, - PredicateKind::Clause(ty::Clause::Projection(p)) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => { + PredicateKind::Clause(ty::Clause::Projection(p)) + if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => + { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? return None; @@ -992,14 +1007,12 @@ pub fn make_projection<'tcx>( debug_assert!( generic_count == substs.len(), - "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\ + "wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\ note: the expected parameters are: {:#?}\n\ - the given arguments are: `{:#?}`", + the given arguments are: `{substs:#?}`", assoc_item.def_id, substs.len(), - generic_count, params.map(ty::GenericParamDefKind::descr).collect::>(), - substs, ); if let Some((idx, (param, arg))) = params @@ -1017,14 +1030,11 @@ pub fn make_projection<'tcx>( { debug_assert!( false, - "mismatched subst type at index {}: expected a {}, found `{:?}`\n\ + "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\ note: the expected parameters are {:#?}\n\ - the given arguments are {:#?}", - idx, + the given arguments are {substs:#?}", param.descr(), - arg, - params.map(ty::GenericParamDefKind::descr).collect::>(), - substs, + params.map(ty::GenericParamDefKind::descr).collect::>() ); } } diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index d4294f18fd50..863fb60fcfca 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -170,22 +170,22 @@ where cb: F, } - struct WithStmtGuarg<'a, F> { + struct WithStmtGuard<'a, F> { val: &'a mut RetFinder, prev_in_stmt: bool, } impl RetFinder { - fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { + fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> { let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); - WithStmtGuarg { + WithStmtGuard { val: self, prev_in_stmt, } } } - impl std::ops::Deref for WithStmtGuarg<'_, F> { + impl std::ops::Deref for WithStmtGuard<'_, F> { type Target = RetFinder; fn deref(&self) -> &Self::Target { @@ -193,13 +193,13 @@ where } } - impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + impl std::ops::DerefMut for WithStmtGuard<'_, F> { fn deref_mut(&mut self) -> &mut Self::Target { self.val } } - impl Drop for WithStmtGuarg<'_, F> { + impl Drop for WithStmtGuard<'_, F> { fn drop(&mut self) { self.val.in_stmt = self.prev_in_stmt; } diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index ee8ab7c1d7cb..bd49f0960726 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -120,8 +120,8 @@ impl ClippyWarning { format!("$CARGO_HOME/{}", stripped.display()) } else { format!( - "target/lintcheck/sources/{}-{}/{}", - crate_name, crate_version, span.file_name + "target/lintcheck/sources/{crate_name}-{crate_version}/{}", + span.file_name ) }; @@ -322,13 +322,13 @@ impl Crate { if config.max_jobs == 1 { println!( - "{}/{} {}% Linting {} {}", - index, total_crates_to_lint, perc, &self.name, &self.version + "{index}/{total_crates_to_lint} {perc}% Linting {} {}", + &self.name, &self.version ); } else { println!( - "{}/{} {}% Linting {} {} in target dir {:?}", - index, total_crates_to_lint, perc, &self.name, &self.version, thread_index + "{index}/{total_crates_to_lint} {perc}% Linting {} {} in target dir {thread_index:?}", + &self.name, &self.version ); } @@ -398,8 +398,7 @@ impl Crate { .output() .unwrap_or_else(|error| { panic!( - "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n", - error, + "Encountered error:\n{error:?}\ncargo_clippy_path: {}\ncrate path:{}\n", &cargo_clippy_path.display(), &self.path.display() ); diff --git a/rust-toolchain b/rust-toolchain index a806c1564796..19fee38db46e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-11-21" +channel = "nightly-2022-12-01" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/driver.rs b/src/driver.rs index ad6132a49baa..9ec4df8e651b 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,6 +1,7 @@ #![feature(rustc_private)] #![feature(let_chains)] #![feature(once_cell)] +#![feature(lint_reasons)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -90,6 +91,10 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option) { // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever // it is rebuilt + #[expect( + clippy::collapsible_if, + reason = "Due to a bug in let_chains this if statement can't be collapsed" + )] if cfg!(debug_assertions) { if let Ok(current_exe) = env::current_exe() && let Some(current_exe) = current_exe.to_str() diff --git a/tests/ui-internal/invalid_msrv_attr_impl.fixed b/tests/ui-internal/invalid_msrv_attr_impl.fixed index 900a8fffd408..08634063a575 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.fixed +++ b/tests/ui-internal/invalid_msrv_attr_impl.fixed @@ -11,9 +11,9 @@ extern crate rustc_middle; #[macro_use] extern crate rustc_session; use clippy_utils::extract_msrv_attr; +use clippy_utils::msrvs::Msrv; use rustc_hir::Expr; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_semver::RustcVersion; declare_lint! { pub TEST_LINT, @@ -22,7 +22,7 @@ declare_lint! { } struct Pass { - msrv: Option, + msrv: Msrv, } impl_lint_pass!(Pass => [TEST_LINT]); diff --git a/tests/ui-internal/invalid_msrv_attr_impl.rs b/tests/ui-internal/invalid_msrv_attr_impl.rs index 4bc8164db67b..f8af77e6d395 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.rs +++ b/tests/ui-internal/invalid_msrv_attr_impl.rs @@ -11,9 +11,9 @@ extern crate rustc_middle; #[macro_use] extern crate rustc_session; use clippy_utils::extract_msrv_attr; +use clippy_utils::msrvs::Msrv; use rustc_hir::Expr; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_semver::RustcVersion; declare_lint! { pub TEST_LINT, @@ -22,7 +22,7 @@ declare_lint! { } struct Pass { - msrv: Option, + msrv: Msrv, } impl_lint_pass!(Pass => [TEST_LINT]); diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 8bfc060e9910..2a240cc249b0 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -1,12 +1,3 @@ -error: hardcoded path to a language item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 - | -LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `LangItem::DerefMut` - = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` - error: hardcoded path to a diagnostic item --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 | @@ -14,6 +5,7 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: convert all references to use `sym::Deref` + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` error: hardcoded path to a diagnostic item --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 @@ -23,5 +15,13 @@ LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", | = help: convert all references to use `sym::deref_method` +error: hardcoded path to a language item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 + | +LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `LangItem::DerefMut` + error: aborting due to 3 previous errors diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml b/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml new file mode 100644 index 000000000000..b95e806aae24 --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml @@ -0,0 +1 @@ +allow-mixed-uninlined-format-args = false diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed new file mode 100644 index 000000000000..aa8b45b5fe7d --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed @@ -0,0 +1,14 @@ +// run-rustfix +#![warn(clippy::uninlined_format_args)] + +fn main() { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + + println!("val='{local_i32}'"); + println!("Hello x is {local_f64:.local_i32$}"); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("{local_i32}, {}", local_opt.unwrap()); +} diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs new file mode 100644 index 000000000000..ad2e4863ee8e --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs @@ -0,0 +1,14 @@ +// run-rustfix +#![warn(clippy::uninlined_format_args)] + +fn main() { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + + println!("val='{}'", local_i32); + println!("Hello {} is {:.*}", "x", local_i32, local_f64); + println!("Hello {} is {:.*}", local_i32, 5, local_f64); + println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + println!("{}, {}", local_i32, local_opt.unwrap()); +} diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr new file mode 100644 index 000000000000..ee9417621961 --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr @@ -0,0 +1,76 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:9:5 + | +LL | println!("val='{}'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: literal with an empty format string + --> $DIR/uninlined_format_args.rs:10:35 + | +LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); + | ^^^ + | + = note: `-D clippy::print-literal` implied by `-D warnings` +help: try this + | +LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); +LL + println!("Hello x is {:.*}", local_i32, local_f64); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:10:5 + | +LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); +LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:11:5 + | +LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:12:5 + | +LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:13:5 + | +LL | println!("{}, {}", local_i32, local_opt.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}, {}", local_i32, local_opt.unwrap()); +LL + println!("{local_i32}, {}", local_opt.unwrap()); + | + +error: aborting due to 6 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index f91d285c2e0e..01a5e962c949 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,6 +1,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of allow-dbg-in-tests allow-expect-in-tests + allow-mixed-uninlined-format-args allow-print-in-tests allow-unwrap-in-tests allowed-scripts diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed index 079b7c000dce..adcbd4d5134d 100644 --- a/tests/ui/almost_complete_letter_range.fixed +++ b/tests/ui/almost_complete_letter_range.fixed @@ -2,7 +2,6 @@ // edition:2018 // aux-build:macro_rules.rs -#![feature(custom_inner_attributes)] #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] @@ -62,16 +61,16 @@ fn main() { b!(); } +#[clippy::msrv = "1.25"] fn _under_msrv() { - #![clippy::msrv = "1.25"] let _ = match 'a' { 'a'...'z' => 1, _ => 2, }; } +#[clippy::msrv = "1.26"] fn _meets_msrv() { - #![clippy::msrv = "1.26"] let _ = 'a'..='z'; let _ = match 'a' { 'a'..='z' => 1, diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs index a66900a976ef..9979316eca42 100644 --- a/tests/ui/almost_complete_letter_range.rs +++ b/tests/ui/almost_complete_letter_range.rs @@ -2,7 +2,6 @@ // edition:2018 // aux-build:macro_rules.rs -#![feature(custom_inner_attributes)] #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] @@ -62,16 +61,16 @@ fn main() { b!(); } +#[clippy::msrv = "1.25"] fn _under_msrv() { - #![clippy::msrv = "1.25"] let _ = match 'a' { 'a'..'z' => 1, _ => 2, }; } +#[clippy::msrv = "1.26"] fn _meets_msrv() { - #![clippy::msrv = "1.26"] let _ = 'a'..'z'; let _ = match 'a' { 'a'..'z' => 1, diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr index 3de44c72c1b9..9abf6d6c5e7d 100644 --- a/tests/ui/almost_complete_letter_range.stderr +++ b/tests/ui/almost_complete_letter_range.stderr @@ -1,5 +1,5 @@ error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:30:17 + --> $DIR/almost_complete_letter_range.rs:29:17 | LL | let _ = ('a') ..'z'; | ^^^^^^--^^^ @@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z'; = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:31:17 + --> $DIR/almost_complete_letter_range.rs:30:17 | LL | let _ = 'A' .. ('Z'); | ^^^^--^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z'); | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:37:13 + --> $DIR/almost_complete_letter_range.rs:36:13 | LL | let _ = (b'a')..(b'z'); | ^^^^^^--^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = (b'a')..(b'z'); | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:38:13 + --> $DIR/almost_complete_letter_range.rs:37:13 | LL | let _ = b'A'..b'Z'; | ^^^^--^^^^ @@ -33,7 +33,7 @@ LL | let _ = b'A'..b'Z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:43:13 + --> $DIR/almost_complete_letter_range.rs:42:13 | LL | let _ = a!()..'z'; | ^^^^--^^^ @@ -41,7 +41,7 @@ LL | let _ = a!()..'z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:46:9 + --> $DIR/almost_complete_letter_range.rs:45:9 | LL | b'a'..b'z' if true => 1, | ^^^^--^^^^ @@ -49,7 +49,7 @@ LL | b'a'..b'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:47:9 + --> $DIR/almost_complete_letter_range.rs:46:9 | LL | b'A'..b'Z' if true => 2, | ^^^^--^^^^ @@ -57,7 +57,7 @@ LL | b'A'..b'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:54:9 + --> $DIR/almost_complete_letter_range.rs:53:9 | LL | 'a'..'z' if true => 1, | ^^^--^^^ @@ -65,7 +65,7 @@ LL | 'a'..'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:55:9 + --> $DIR/almost_complete_letter_range.rs:54:9 | LL | 'A'..'Z' if true => 2, | ^^^--^^^ @@ -73,7 +73,7 @@ LL | 'A'..'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:23:17 + --> $DIR/almost_complete_letter_range.rs:22:17 | LL | let _ = 'a'..'z'; | ^^^--^^^ @@ -86,7 +86,7 @@ LL | b!(); = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:68:9 + --> $DIR/almost_complete_letter_range.rs:67:9 | LL | 'a'..'z' => 1, | ^^^--^^^ @@ -94,7 +94,7 @@ LL | 'a'..'z' => 1, | help: use an inclusive range: `...` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:75:13 + --> $DIR/almost_complete_letter_range.rs:74:13 | LL | let _ = 'a'..'z'; | ^^^--^^^ @@ -102,7 +102,7 @@ LL | let _ = 'a'..'z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:77:9 + --> $DIR/almost_complete_letter_range.rs:76:9 | LL | 'a'..'z' => 1, | ^^^--^^^ diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index e6bf944c7a5e..8676b562b4f9 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] #![allow(clippy::uninlined_format_args, unused)] @@ -33,16 +32,14 @@ fn main() { let _ = (x as i64 - y as i64).unsigned_abs() as u32; } +#[clippy::msrv = "1.50"] fn msrv_1_50() { - #![clippy::msrv = "1.50"] - let x: i32 = 10; assert_eq!(10u32, x.abs() as u32); } +#[clippy::msrv = "1.51"] fn msrv_1_51() { - #![clippy::msrv = "1.51"] - let x: i32 = 10; assert_eq!(10u32, x.unsigned_abs()); } diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index c87320b5209d..5775af874f8f 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::cast_abs_to_unsigned)] #![allow(clippy::uninlined_format_args, unused)] @@ -33,16 +32,14 @@ fn main() { let _ = (x as i64 - y as i64).abs() as u32; } +#[clippy::msrv = "1.50"] fn msrv_1_50() { - #![clippy::msrv = "1.50"] - let x: i32 = 10; assert_eq!(10u32, x.abs() as u32); } +#[clippy::msrv = "1.51"] fn msrv_1_51() { - #![clippy::msrv = "1.51"] - let x: i32 = 10; assert_eq!(10u32, x.abs() as u32); } diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 1b39c554b038..4668554f4511 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:9:18 + --> $DIR/cast_abs_to_unsigned.rs:8:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,103 +7,103 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:13:20 + --> $DIR/cast_abs_to_unsigned.rs:12:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:14:20 + --> $DIR/cast_abs_to_unsigned.rs:13:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:15:13 + --> $DIR/cast_abs_to_unsigned.rs:14:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:17:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:21:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:22:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:23:13 + --> $DIR/cast_abs_to_unsigned.rs:22:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:25:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:29:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:30:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:31:13 + --> $DIR/cast_abs_to_unsigned.rs:30:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:33:13 + --> $DIR/cast_abs_to_unsigned.rs:32:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:47:23 + --> $DIR/cast_abs_to_unsigned.rs:44:23 | LL | assert_eq!(10u32, x.abs() as u32); | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` diff --git a/tests/ui/cast_lossless_bool.fixed b/tests/ui/cast_lossless_bool.fixed index af13b755e310..13b3cf838c9f 100644 --- a/tests/ui/cast_lossless_bool.fixed +++ b/tests/ui/cast_lossless_bool.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -42,14 +41,12 @@ mod cast_lossless_in_impl { } } +#[clippy::msrv = "1.27"] fn msrv_1_27() { - #![clippy::msrv = "1.27"] - let _ = true as u8; } +#[clippy::msrv = "1.28"] fn msrv_1_28() { - #![clippy::msrv = "1.28"] - let _ = u8::from(true); } diff --git a/tests/ui/cast_lossless_bool.rs b/tests/ui/cast_lossless_bool.rs index 3b06af899c60..3eed2135562c 100644 --- a/tests/ui/cast_lossless_bool.rs +++ b/tests/ui/cast_lossless_bool.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(dead_code)] #![warn(clippy::cast_lossless)] @@ -42,14 +41,12 @@ mod cast_lossless_in_impl { } } +#[clippy::msrv = "1.27"] fn msrv_1_27() { - #![clippy::msrv = "1.27"] - let _ = true as u8; } +#[clippy::msrv = "1.28"] fn msrv_1_28() { - #![clippy::msrv = "1.28"] - let _ = true as u8; } diff --git a/tests/ui/cast_lossless_bool.stderr b/tests/ui/cast_lossless_bool.stderr index 768b033d10a2..ce240b70f891 100644 --- a/tests/ui/cast_lossless_bool.stderr +++ b/tests/ui/cast_lossless_bool.stderr @@ -1,5 +1,5 @@ error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> $DIR/cast_lossless_bool.rs:9:13 + --> $DIR/cast_lossless_bool.rs:8:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` @@ -7,79 +7,79 @@ LL | let _ = true as u8; = note: `-D clippy::cast-lossless` implied by `-D warnings` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:10:13 + --> $DIR/cast_lossless_bool.rs:9:13 | LL | let _ = true as u16; | ^^^^^^^^^^^ help: try: `u16::from(true)` error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)` - --> $DIR/cast_lossless_bool.rs:11:13 + --> $DIR/cast_lossless_bool.rs:10:13 | LL | let _ = true as u32; | ^^^^^^^^^^^ help: try: `u32::from(true)` error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)` - --> $DIR/cast_lossless_bool.rs:12:13 + --> $DIR/cast_lossless_bool.rs:11:13 | LL | let _ = true as u64; | ^^^^^^^^^^^ help: try: `u64::from(true)` error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)` - --> $DIR/cast_lossless_bool.rs:13:13 + --> $DIR/cast_lossless_bool.rs:12:13 | LL | let _ = true as u128; | ^^^^^^^^^^^^ help: try: `u128::from(true)` error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)` - --> $DIR/cast_lossless_bool.rs:14:13 + --> $DIR/cast_lossless_bool.rs:13:13 | LL | let _ = true as usize; | ^^^^^^^^^^^^^ help: try: `usize::from(true)` error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)` - --> $DIR/cast_lossless_bool.rs:16:13 + --> $DIR/cast_lossless_bool.rs:15:13 | LL | let _ = true as i8; | ^^^^^^^^^^ help: try: `i8::from(true)` error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)` - --> $DIR/cast_lossless_bool.rs:17:13 + --> $DIR/cast_lossless_bool.rs:16:13 | LL | let _ = true as i16; | ^^^^^^^^^^^ help: try: `i16::from(true)` error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)` - --> $DIR/cast_lossless_bool.rs:18:13 + --> $DIR/cast_lossless_bool.rs:17:13 | LL | let _ = true as i32; | ^^^^^^^^^^^ help: try: `i32::from(true)` error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)` - --> $DIR/cast_lossless_bool.rs:19:13 + --> $DIR/cast_lossless_bool.rs:18:13 | LL | let _ = true as i64; | ^^^^^^^^^^^ help: try: `i64::from(true)` error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)` - --> $DIR/cast_lossless_bool.rs:20:13 + --> $DIR/cast_lossless_bool.rs:19:13 | LL | let _ = true as i128; | ^^^^^^^^^^^^ help: try: `i128::from(true)` error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)` - --> $DIR/cast_lossless_bool.rs:21:13 + --> $DIR/cast_lossless_bool.rs:20:13 | LL | let _ = true as isize; | ^^^^^^^^^^^^^ help: try: `isize::from(true)` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> $DIR/cast_lossless_bool.rs:24:13 + --> $DIR/cast_lossless_bool.rs:23:13 | LL | let _ = (true | false) as u16; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> $DIR/cast_lossless_bool.rs:54:13 + --> $DIR/cast_lossless_bool.rs:51:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` diff --git a/tests/ui/cfg_attr_rustfmt.fixed b/tests/ui/cfg_attr_rustfmt.fixed index 8a5645b22ed1..b970b1209b65 100644 --- a/tests/ui/cfg_attr_rustfmt.fixed +++ b/tests/ui/cfg_attr_rustfmt.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes, custom_inner_attributes)] +#![feature(stmt_expr_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -30,16 +30,14 @@ mod foo { pub fn f() {} } +#[clippy::msrv = "1.29"] fn msrv_1_29() { - #![clippy::msrv = "1.29"] - #[cfg_attr(rustfmt, rustfmt::skip)] 1+29; } +#[clippy::msrv = "1.30"] fn msrv_1_30() { - #![clippy::msrv = "1.30"] - #[rustfmt::skip] 1+30; } diff --git a/tests/ui/cfg_attr_rustfmt.rs b/tests/ui/cfg_attr_rustfmt.rs index 2fb140efae76..0a8e6a89d8a0 100644 --- a/tests/ui/cfg_attr_rustfmt.rs +++ b/tests/ui/cfg_attr_rustfmt.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(stmt_expr_attributes, custom_inner_attributes)] +#![feature(stmt_expr_attributes)] #![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] #![warn(clippy::deprecated_cfg_attr)] @@ -30,16 +30,14 @@ mod foo { pub fn f() {} } +#[clippy::msrv = "1.29"] fn msrv_1_29() { - #![clippy::msrv = "1.29"] - #[cfg_attr(rustfmt, rustfmt::skip)] 1+29; } +#[clippy::msrv = "1.30"] fn msrv_1_30() { - #![clippy::msrv = "1.30"] - #[cfg_attr(rustfmt, rustfmt::skip)] 1+30; } diff --git a/tests/ui/cfg_attr_rustfmt.stderr b/tests/ui/cfg_attr_rustfmt.stderr index 08df7b2b39a0..524a2bf72484 100644 --- a/tests/ui/cfg_attr_rustfmt.stderr +++ b/tests/ui/cfg_attr_rustfmt.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(rustfmt, rustfmt_skip)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes - --> $DIR/cfg_attr_rustfmt.rs:43:5 + --> $DIR/cfg_attr_rustfmt.rs:41:5 | LL | #[cfg_attr(rustfmt, rustfmt::skip)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index f936957cb40c..e279ba3147bb 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, unused, @@ -78,16 +77,14 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +#[clippy::msrv = "1.33"] fn msrv_1_33() { - #![clippy::msrv = "1.33"] - let value: i64 = 33; let _ = value <= (u32::MAX as i64) && value >= 0; } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let value: i64 = 34; let _ = u32::try_from(value).is_ok(); } diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index 77aec713ff31..9d7a40995c37 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow( clippy::cast_lossless, unused, @@ -78,16 +77,14 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +#[clippy::msrv = "1.33"] fn msrv_1_33() { - #![clippy::msrv = "1.33"] - let value: i64 = 33; let _ = value <= (u32::MAX as i64) && value >= 0; } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let value: i64 = 34; let _ = value <= (u32::MAX as i64) && value >= 0; } diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index b2bf7af8daf8..273ead73bda5 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:17:13 + --> $DIR/checked_conversions.rs:16:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,97 +7,97 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = note: `-D clippy::checked-conversions` implied by `-D warnings` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:18:13 + --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:22:13 + --> $DIR/checked_conversions.rs:21:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:23:13 + --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:27:13 + --> $DIR/checked_conversions.rs:26:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:28:13 + --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:34:13 + --> $DIR/checked_conversions.rs:33:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:35:13 + --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:39:13 + --> $DIR/checked_conversions.rs:38:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:40:13 + --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:46:13 + --> $DIR/checked_conversions.rs:45:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:47:13 + --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:51:13 + --> $DIR/checked_conversions.rs:50:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:52:13 + --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:56:13 + --> $DIR/checked_conversions.rs:55:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:57:13 + --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:92:13 + --> $DIR/checked_conversions.rs:89:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed index 42ed232d1001..ecbfc1feedf6 100644 --- a/tests/ui/cloned_instead_of_copied.fixed +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] #![allow(unused)] @@ -17,23 +16,20 @@ fn main() { let _ = Some(&String::new()).cloned(); } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let _ = [1].iter().cloned(); let _ = Some(&1).cloned(); } +#[clippy::msrv = "1.35"] fn msrv_1_35() { - #![clippy::msrv = "1.35"] - let _ = [1].iter().cloned(); let _ = Some(&1).copied(); // Option::copied needs 1.35 } +#[clippy::msrv = "1.36"] fn msrv_1_36() { - #![clippy::msrv = "1.36"] - let _ = [1].iter().copied(); // Iterator::copied needs 1.36 let _ = Some(&1).copied(); } diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs index 471bd9654cc1..163dc3dddce6 100644 --- a/tests/ui/cloned_instead_of_copied.rs +++ b/tests/ui/cloned_instead_of_copied.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::cloned_instead_of_copied)] #![allow(unused)] @@ -17,23 +16,20 @@ fn main() { let _ = Some(&String::new()).cloned(); } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let _ = [1].iter().cloned(); let _ = Some(&1).cloned(); } +#[clippy::msrv = "1.35"] fn msrv_1_35() { - #![clippy::msrv = "1.35"] - let _ = [1].iter().cloned(); let _ = Some(&1).cloned(); // Option::copied needs 1.35 } +#[clippy::msrv = "1.36"] fn msrv_1_36() { - #![clippy::msrv = "1.36"] - let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 let _ = Some(&1).cloned(); } diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr index 914c9a91e830..e0361acd9560 100644 --- a/tests/ui/cloned_instead_of_copied.stderr +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -1,5 +1,5 @@ error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:9:24 + --> $DIR/cloned_instead_of_copied.rs:8:24 | LL | let _ = [1].iter().cloned(); | ^^^^^^ help: try: `copied` @@ -7,43 +7,43 @@ LL | let _ = [1].iter().cloned(); = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:10:31 + --> $DIR/cloned_instead_of_copied.rs:9:31 | LL | let _ = vec!["hi"].iter().cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:11:22 + --> $DIR/cloned_instead_of_copied.rs:10:22 | LL | let _ = Some(&1).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:12:34 + --> $DIR/cloned_instead_of_copied.rs:11:34 | LL | let _ = Box::new([1].iter()).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:13:32 + --> $DIR/cloned_instead_of_copied.rs:12:32 | LL | let _ = Box::new(Some(&1)).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:31:22 + --> $DIR/cloned_instead_of_copied.rs:28:22 | LL | let _ = Some(&1).cloned(); // Option::copied needs 1.35 | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:37:24 + --> $DIR/cloned_instead_of_copied.rs:33:24 | LL | let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:38:22 + --> $DIR/cloned_instead_of_copied.rs:34:22 | LL | let _ = Some(&1).cloned(); | ^^^^^^ help: try: `copied` diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed index 3bac738acd65..b63cbd8a8e6b 100644 --- a/tests/ui/err_expect.fixed +++ b/tests/ui/err_expect.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] struct MyTypeNonDebug; @@ -16,16 +15,14 @@ fn main() { test_non_debug.err().expect("Testing non debug type"); } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - let x: Result = Ok(16); x.err().expect("16"); } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - let x: Result = Ok(17); x.expect_err("17"); } diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs index 6e7c47d9ad3c..c081a745fb40 100644 --- a/tests/ui/err_expect.rs +++ b/tests/ui/err_expect.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] struct MyTypeNonDebug; @@ -16,16 +15,14 @@ fn main() { test_non_debug.err().expect("Testing non debug type"); } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - let x: Result = Ok(16); x.err().expect("16"); } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - let x: Result = Ok(17); x.err().expect("17"); } diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr index 91a6cf8de65f..82c0754cfb00 100644 --- a/tests/ui/err_expect.stderr +++ b/tests/ui/err_expect.stderr @@ -1,5 +1,5 @@ error: called `.err().expect()` on a `Result` value - --> $DIR/err_expect.rs:13:16 + --> $DIR/err_expect.rs:12:16 | LL | test_debug.err().expect("Testing debug type"); | ^^^^^^^^^^^^ help: try: `expect_err` @@ -7,7 +7,7 @@ LL | test_debug.err().expect("Testing debug type"); = note: `-D clippy::err-expect` implied by `-D warnings` error: called `.err().expect()` on a `Result` value - --> $DIR/err_expect.rs:30:7 + --> $DIR/err_expect.rs:27:7 | LL | x.err().expect("17"); | ^^^^^^^^^^^^ help: try: `expect_err` diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index a9cc80aaaf62..dc129591eac4 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -316,3 +316,25 @@ pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) - move || takes_fn_mut(&mut f_used_once) } + +impl dyn TestTrait + '_ { + fn method_on_dyn(&self) -> bool { + false + } +} + +// https://github.com/rust-lang/rust-clippy/issues/7746 +fn angle_brackets_and_substs() { + let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]); + array_opt.map(<[u8; 3]>::as_slice); + + let slice_opt: Option<&[u8]> = Some(b"slice"); + slice_opt.map(<[u8]>::len); + + let ptr_opt: Option<*const usize> = Some(&487); + ptr_opt.map(<*const usize>::is_null); + + let test_struct = TestStruct { some_ref: &487 }; + let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct); + dyn_opt.map(::method_on_dyn); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index cc99906ccd66..025fd6a0b7af 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -316,3 +316,25 @@ pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) - move || takes_fn_mut(|| f_used_once()) } + +impl dyn TestTrait + '_ { + fn method_on_dyn(&self) -> bool { + false + } +} + +// https://github.com/rust-lang/rust-clippy/issues/7746 +fn angle_brackets_and_substs() { + let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]); + array_opt.map(|a| a.as_slice()); + + let slice_opt: Option<&[u8]> = Some(b"slice"); + slice_opt.map(|s| s.len()); + + let ptr_opt: Option<*const usize> = Some(&487); + ptr_opt.map(|p| p.is_null()); + + let test_struct = TestStruct { some_ref: &487 }; + let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct); + dyn_opt.map(|d| d.method_on_dyn()); +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 434706b7e258..a521fb868607 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -134,5 +134,29 @@ error: redundant closure LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` -error: aborting due to 22 previous errors +error: redundant closure + --> $DIR/eta.rs:329:19 + | +LL | array_opt.map(|a| a.as_slice()); + | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` + +error: redundant closure + --> $DIR/eta.rs:332:19 + | +LL | slice_opt.map(|s| s.len()); + | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` + +error: redundant closure + --> $DIR/eta.rs:335:17 + | +LL | ptr_opt.map(|p| p.is_null()); + | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` + +error: redundant closure + --> $DIR/eta.rs:339:17 + | +LL | dyn_opt.map(|d| d.method_on_dyn()); + | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `::method_on_dyn` + +error: aborting due to 26 previous errors diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index 59ff5e4040a3..475fae5e823b 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -277,4 +277,8 @@ fn main() { unimplemented!() } let _: String = takes_assoc(&*String::new()); + + // Issue #9901 + fn takes_ref(_: &i32) {} + takes_ref(*Box::new(&0i32)); } diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index bcfb60c32788..c1894258f4d8 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -277,4 +277,8 @@ fn main() { unimplemented!() } let _: String = takes_assoc(&*String::new()); + + // Issue #9901 + fn takes_ref(_: &i32) {} + takes_ref(*Box::new(&0i32)); } diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed index 41828ddd7acd..462d46169fcb 100644 --- a/tests/ui/filter_map_next_fixable.fixed +++ b/tests/ui/filter_map_next_fixable.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] #![allow(unused)] @@ -11,16 +10,14 @@ fn main() { assert_eq!(element, Some(1)); } +#[clippy::msrv = "1.29"] fn msrv_1_29() { - #![clippy::msrv = "1.29"] - let a = ["1", "lol", "3", "NaN", "5"]; let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); } +#[clippy::msrv = "1.30"] fn msrv_1_30() { - #![clippy::msrv = "1.30"] - let a = ["1", "lol", "3", "NaN", "5"]; let _: Option = a.iter().find_map(|s| s.parse().ok()); } diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs index be492a81b45e..2ea00cf73072 100644 --- a/tests/ui/filter_map_next_fixable.rs +++ b/tests/ui/filter_map_next_fixable.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::all, clippy::pedantic)] #![allow(unused)] @@ -11,16 +10,14 @@ fn main() { assert_eq!(element, Some(1)); } +#[clippy::msrv = "1.29"] fn msrv_1_29() { - #![clippy::msrv = "1.29"] - let a = ["1", "lol", "3", "NaN", "5"]; let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); } +#[clippy::msrv = "1.30"] fn msrv_1_30() { - #![clippy::msrv = "1.30"] - let a = ["1", "lol", "3", "NaN", "5"]; let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); } diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr index e789efeabd55..a9fc6abe88fa 100644 --- a/tests/ui/filter_map_next_fixable.stderr +++ b/tests/ui/filter_map_next_fixable.stderr @@ -1,5 +1,5 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> $DIR/filter_map_next_fixable.rs:10:32 + --> $DIR/filter_map_next_fixable.rs:9:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` @@ -7,7 +7,7 @@ LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next = note: `-D clippy::filter-map-next` implied by `-D warnings` error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> $DIR/filter_map_next_fixable.rs:25:26 + --> $DIR/filter_map_next_fixable.rs:22:26 | LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed index 1cf49ca45f49..125c9a69cd3f 100644 --- a/tests/ui/from_over_into.fixed +++ b/tests/ui/from_over_into.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -60,9 +59,8 @@ impl From for A { } } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - struct FromOverInto(Vec); impl Into> for Vec { @@ -72,9 +70,8 @@ fn msrv_1_40() { } } +#[clippy::msrv = "1.41"] fn msrv_1_41() { - #![clippy::msrv = "1.41"] - struct FromOverInto(Vec); impl From> for FromOverInto { diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index d30f3c3fc925..5aa127bfabe4 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -60,9 +59,8 @@ impl From for A { } } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - struct FromOverInto(Vec); impl Into> for Vec { @@ -72,9 +70,8 @@ fn msrv_1_40() { } } +#[clippy::msrv = "1.41"] fn msrv_1_41() { - #![clippy::msrv = "1.41"] - struct FromOverInto(Vec); impl Into> for Vec { diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 9c2a7c04c364..a1764a5ea12a 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,5 +1,5 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:10:1 + --> $DIR/from_over_into.rs:9:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL ~ StringWrapper(val) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:18:1 + --> $DIR/from_over_into.rs:17:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL ~ SelfType(String::new()) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:33:1 + --> $DIR/from_over_into.rs:32:1 | LL | impl Into for X { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL ~ let _: X = val; | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:45:1 + --> $DIR/from_over_into.rs:44:1 | LL | impl core::convert::Into for crate::ExplicitPaths { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ LL ~ val.0 | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:80:5 + --> $DIR/from_over_into.rs:77:5 | LL | impl Into> for Vec { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 3bc3a0395244..0e89fdb0dfa2 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -1,5 +1,4 @@ #![warn(clippy::if_then_some_else_none)] -#![feature(custom_inner_attributes)] fn main() { // Should issue an error. @@ -66,8 +65,8 @@ fn main() { let _ = if foo() { into_some("foo") } else { None }; } +#[clippy::msrv = "1.49"] fn _msrv_1_49() { - #![clippy::msrv = "1.49"] // `bool::then` was stabilized in 1.50. Do not lint this let _ = if foo() { println!("true!"); @@ -77,8 +76,8 @@ fn _msrv_1_49() { }; } +#[clippy::msrv = "1.50"] fn _msrv_1_50() { - #![clippy::msrv = "1.50"] let _ = if foo() { println!("true!"); Some(150) diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index 24e0b5947f19..d728a3c31a3b 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -1,5 +1,5 @@ error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:6:13 + --> $DIR/if_then_some_else_none.rs:5:13 | LL | let _ = if foo() { | _____________^ @@ -14,7 +14,7 @@ LL | | }; = note: `-D clippy::if-then-some-else-none` implied by `-D warnings` error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:14:13 + --> $DIR/if_then_some_else_none.rs:13:13 | LL | let _ = if matches!(true, true) { | _____________^ @@ -28,7 +28,7 @@ LL | | }; = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })` error: this could be simplified with `bool::then_some` - --> $DIR/if_then_some_else_none.rs:23:28 + --> $DIR/if_then_some_else_none.rs:22:28 | LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); = help: consider using `bool::then_some` like: `(o < 32).then_some(o)` error: this could be simplified with `bool::then_some` - --> $DIR/if_then_some_else_none.rs:27:13 + --> $DIR/if_then_some_else_none.rs:26:13 | LL | let _ = if !x { Some(0) } else { None }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _ = if !x { Some(0) } else { None }; = help: consider using `bool::then_some` like: `(!x).then_some(0)` error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:82:13 + --> $DIR/if_then_some_else_none.rs:81:13 | LL | let _ = if foo() { | _____________^ diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs index 331fd29b74e8..f7902e6fd538 100644 --- a/tests/ui/manual_clamp.rs +++ b/tests/ui/manual_clamp.rs @@ -1,4 +1,3 @@ -#![feature(custom_inner_attributes)] #![warn(clippy::manual_clamp)] #![allow( unused, @@ -304,9 +303,8 @@ fn cmp_min_max(input: i32) -> i32 { input * 3 } +#[clippy::msrv = "1.49"] fn msrv_1_49() { - #![clippy::msrv = "1.49"] - let (input, min, max) = (0, -1, 2); let _ = if input < min { min @@ -317,9 +315,8 @@ fn msrv_1_49() { }; } +#[clippy::msrv = "1.50"] fn msrv_1_50() { - #![clippy::msrv = "1.50"] - let (input, min, max) = (0, -1, 2); let _ = if input < min { min diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr index 70abe28091c9..988ad1527e83 100644 --- a/tests/ui/manual_clamp.stderr +++ b/tests/ui/manual_clamp.stderr @@ -1,5 +1,5 @@ error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:77:5 + --> $DIR/manual_clamp.rs:76:5 | LL | / if x9 < min { LL | | x9 = min; @@ -13,7 +13,7 @@ LL | | } = note: `-D clippy::manual-clamp` implied by `-D warnings` error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:92:5 + --> $DIR/manual_clamp.rs:91:5 | LL | / if x11 > max { LL | | x11 = max; @@ -26,7 +26,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:100:5 + --> $DIR/manual_clamp.rs:99:5 | LL | / if min > x12 { LL | | x12 = min; @@ -39,7 +39,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:108:5 + --> $DIR/manual_clamp.rs:107:5 | LL | / if max < x13 { LL | | x13 = max; @@ -52,7 +52,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:162:5 + --> $DIR/manual_clamp.rs:161:5 | LL | / if max < x33 { LL | | x33 = max; @@ -65,7 +65,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:22:14 + --> $DIR/manual_clamp.rs:21:14 | LL | let x0 = if max < input { | ______________^ @@ -80,7 +80,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:30:14 + --> $DIR/manual_clamp.rs:29:14 | LL | let x1 = if input > max { | ______________^ @@ -95,7 +95,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:38:14 + --> $DIR/manual_clamp.rs:37:14 | LL | let x2 = if input < min { | ______________^ @@ -110,7 +110,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:46:14 + --> $DIR/manual_clamp.rs:45:14 | LL | let x3 = if min > input { | ______________^ @@ -125,7 +125,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:54:14 + --> $DIR/manual_clamp.rs:53:14 | LL | let x4 = input.max(min).min(max); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -133,7 +133,7 @@ LL | let x4 = input.max(min).min(max); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:56:14 + --> $DIR/manual_clamp.rs:55:14 | LL | let x5 = input.min(max).max(min); | ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)` @@ -141,7 +141,7 @@ LL | let x5 = input.min(max).max(min); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:58:14 + --> $DIR/manual_clamp.rs:57:14 | LL | let x6 = match input { | ______________^ @@ -154,7 +154,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:64:14 + --> $DIR/manual_clamp.rs:63:14 | LL | let x7 = match input { | ______________^ @@ -167,7 +167,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:70:14 + --> $DIR/manual_clamp.rs:69:14 | LL | let x8 = match input { | ______________^ @@ -180,7 +180,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:84:15 + --> $DIR/manual_clamp.rs:83:15 | LL | let x10 = match input { | _______________^ @@ -193,7 +193,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:115:15 + --> $DIR/manual_clamp.rs:114:15 | LL | let x14 = if input > CONST_MAX { | _______________^ @@ -208,7 +208,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:124:19 + --> $DIR/manual_clamp.rs:123:19 | LL | let x15 = if input > max { | ___________________^ @@ -224,7 +224,7 @@ LL | | }; = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:135:19 + --> $DIR/manual_clamp.rs:134:19 | LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -232,7 +232,7 @@ LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:136:19 + --> $DIR/manual_clamp.rs:135:19 | LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -240,7 +240,7 @@ LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:137:19 + --> $DIR/manual_clamp.rs:136:19 | LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -248,7 +248,7 @@ LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:138:19 + --> $DIR/manual_clamp.rs:137:19 | LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -256,7 +256,7 @@ LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:139:19 + --> $DIR/manual_clamp.rs:138:19 | LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -264,7 +264,7 @@ LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:140:19 + --> $DIR/manual_clamp.rs:139:19 | LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -272,7 +272,7 @@ LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:141:19 + --> $DIR/manual_clamp.rs:140:19 | LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -280,7 +280,7 @@ LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:142:19 + --> $DIR/manual_clamp.rs:141:19 | LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -288,7 +288,7 @@ LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:144:19 + --> $DIR/manual_clamp.rs:143:19 | LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -297,7 +297,7 @@ LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:145:19 + --> $DIR/manual_clamp.rs:144:19 | LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -306,7 +306,7 @@ LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:146:19 + --> $DIR/manual_clamp.rs:145:19 | LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -315,7 +315,7 @@ LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:147:19 + --> $DIR/manual_clamp.rs:146:19 | LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -324,7 +324,7 @@ LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:148:19 + --> $DIR/manual_clamp.rs:147:19 | LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -333,7 +333,7 @@ LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:149:19 + --> $DIR/manual_clamp.rs:148:19 | LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -342,7 +342,7 @@ LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:150:19 + --> $DIR/manual_clamp.rs:149:19 | LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -351,7 +351,7 @@ LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:151:19 + --> $DIR/manual_clamp.rs:150:19 | LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -360,7 +360,7 @@ LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:154:5 + --> $DIR/manual_clamp.rs:153:5 | LL | / if x32 < min { LL | | x32 = min; @@ -372,7 +372,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> $DIR/manual_clamp.rs:324:13 + --> $DIR/manual_clamp.rs:321:13 | LL | let _ = if input < min { | _____________^ diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index 765bb785994e..231ba83b1426 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused, dead_code)] #![warn(clippy::manual_is_ascii_check)] @@ -18,28 +17,26 @@ fn main() { assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); } +#[clippy::msrv = "1.23"] fn msrv_1_23() { - #![clippy::msrv = "1.23"] - assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); } +#[clippy::msrv = "1.24"] fn msrv_1_24() { - #![clippy::msrv = "1.24"] - assert!(b'1'.is_ascii_digit()); assert!('X'.is_ascii_uppercase()); assert!('x'.is_ascii_alphabetic()); } +#[clippy::msrv = "1.46"] fn msrv_1_46() { - #![clippy::msrv = "1.46"] const FOO: bool = matches!('x', '0'..='9'); } +#[clippy::msrv = "1.47"] fn msrv_1_47() { - #![clippy::msrv = "1.47"] const FOO: bool = 'x'.is_ascii_digit(); } diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index be1331610412..39ee6151c56f 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused, dead_code)] #![warn(clippy::manual_is_ascii_check)] @@ -18,28 +17,26 @@ fn main() { assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); } +#[clippy::msrv = "1.23"] fn msrv_1_23() { - #![clippy::msrv = "1.23"] - assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); } +#[clippy::msrv = "1.24"] fn msrv_1_24() { - #![clippy::msrv = "1.24"] - assert!(matches!(b'1', b'0'..=b'9')); assert!(matches!('X', 'A'..='Z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z')); } +#[clippy::msrv = "1.46"] fn msrv_1_46() { - #![clippy::msrv = "1.46"] const FOO: bool = matches!('x', '0'..='9'); } +#[clippy::msrv = "1.47"] fn msrv_1_47() { - #![clippy::msrv = "1.47"] const FOO: bool = matches!('x', '0'..='9'); } diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index c0a9d4db1a15..397cbe05c822 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -1,5 +1,5 @@ error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:8:13 + --> $DIR/manual_is_ascii_check.rs:7:13 | LL | assert!(matches!('x', 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_lowercase()` @@ -7,61 +7,61 @@ LL | assert!(matches!('x', 'a'..='z')); = note: `-D clippy::manual-is-ascii-check` implied by `-D warnings` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:9:13 + --> $DIR/manual_is_ascii_check.rs:8:13 | LL | assert!(matches!('X', 'A'..='Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:10:13 + --> $DIR/manual_is_ascii_check.rs:9:13 | LL | assert!(matches!(b'x', b'a'..=b'z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'x'.is_ascii_lowercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:11:13 + --> $DIR/manual_is_ascii_check.rs:10:13 | LL | assert!(matches!(b'X', b'A'..=b'Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:14:13 + --> $DIR/manual_is_ascii_check.rs:13:13 | LL | assert!(matches!(num, '0'..='9')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:15:13 + --> $DIR/manual_is_ascii_check.rs:14:13 | LL | assert!(matches!(b'1', b'0'..=b'9')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:16:13 + --> $DIR/manual_is_ascii_check.rs:15:13 | LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:32:13 + --> $DIR/manual_is_ascii_check.rs:29:13 | LL | assert!(matches!(b'1', b'0'..=b'9')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:33:13 + --> $DIR/manual_is_ascii_check.rs:30:13 | LL | assert!(matches!('X', 'A'..='Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:34:13 + --> $DIR/manual_is_ascii_check.rs:31:13 | LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:44:23 + --> $DIR/manual_is_ascii_check.rs:41:23 | LL | const FOO: bool = matches!('x', '0'..='9'); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index 2ef40e5911af..48a162c13602 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -234,4 +234,18 @@ fn not_fire() { // If a type annotation is present, don't lint as // expressing the type might be too hard let v: () = if let Some(v_some) = g() { v_some } else { panic!() }; + + // Issue 9940 + // Suggestion should not expand macros + macro_rules! macro_call { + () => { + return () + }; + } + + let ff = Some(1); + let _ = match ff { + Some(value) => value, + _ => macro_call!(), + }; } diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index 453b68b8bd00..52aac6bc673d 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -259,5 +259,14 @@ LL | create_binding_if_some!(w, g()); | = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 17 previous errors +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:247:5 + | +LL | / let _ = match ff { +LL | | Some(value) => value, +LL | | _ => macro_call!(), +LL | | }; + | |______^ help: consider writing: `let Some(value) = ff else { macro_call!() };` + +error: aborting due to 18 previous errors diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed index b942fbfe9305..4cdc0546a744 100644 --- a/tests/ui/manual_rem_euclid.fixed +++ b/tests/ui/manual_rem_euclid.fixed @@ -1,7 +1,6 @@ // run-rustfix // aux-build:macro_rules.rs -#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -55,31 +54,27 @@ pub const fn const_rem_euclid_4(num: i32) -> i32 { num.rem_euclid(4) } +#[clippy::msrv = "1.37"] pub fn msrv_1_37() { - #![clippy::msrv = "1.37"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } +#[clippy::msrv = "1.38"] pub fn msrv_1_38() { - #![clippy::msrv = "1.38"] - let x: i32 = 10; let _: i32 = x.rem_euclid(4); } // For const fns: +#[clippy::msrv = "1.51"] pub const fn msrv_1_51() { - #![clippy::msrv = "1.51"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } +#[clippy::msrv = "1.52"] pub const fn msrv_1_52() { - #![clippy::msrv = "1.52"] - let x: i32 = 10; let _: i32 = x.rem_euclid(4); } diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs index 7462d532169f..58a9e20f38b1 100644 --- a/tests/ui/manual_rem_euclid.rs +++ b/tests/ui/manual_rem_euclid.rs @@ -1,7 +1,6 @@ // run-rustfix // aux-build:macro_rules.rs -#![feature(custom_inner_attributes)] #![warn(clippy::manual_rem_euclid)] #[macro_use] @@ -55,31 +54,27 @@ pub const fn const_rem_euclid_4(num: i32) -> i32 { ((num % 4) + 4) % 4 } +#[clippy::msrv = "1.37"] pub fn msrv_1_37() { - #![clippy::msrv = "1.37"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } +#[clippy::msrv = "1.38"] pub fn msrv_1_38() { - #![clippy::msrv = "1.38"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } // For const fns: +#[clippy::msrv = "1.51"] pub const fn msrv_1_51() { - #![clippy::msrv = "1.51"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } +#[clippy::msrv = "1.52"] pub const fn msrv_1_52() { - #![clippy::msrv = "1.52"] - let x: i32 = 10; let _: i32 = ((x % 4) + 4) % 4; } diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr index d51bac03b565..e3122a588b64 100644 --- a/tests/ui/manual_rem_euclid.stderr +++ b/tests/ui/manual_rem_euclid.stderr @@ -1,5 +1,5 @@ error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:20:18 + --> $DIR/manual_rem_euclid.rs:19:18 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -7,31 +7,31 @@ LL | let _: i32 = ((value % 4) + 4) % 4; = note: `-D clippy::manual-rem-euclid` implied by `-D warnings` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:21:18 + --> $DIR/manual_rem_euclid.rs:20:18 | LL | let _: i32 = (4 + (value % 4)) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:22:18 + --> $DIR/manual_rem_euclid.rs:21:18 | LL | let _: i32 = (value % 4 + 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:23:18 + --> $DIR/manual_rem_euclid.rs:22:18 | LL | let _: i32 = (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:24:22 + --> $DIR/manual_rem_euclid.rs:23:22 | LL | let _: i32 = 1 + (4 + value % 4) % 4; | ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:13:22 + --> $DIR/manual_rem_euclid.rs:12:22 | LL | let _: i32 = ((value % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)` @@ -42,25 +42,25 @@ LL | internal_rem_euclid!(); = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info) error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:50:5 + --> $DIR/manual_rem_euclid.rs:49:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:55:5 + --> $DIR/manual_rem_euclid.rs:54:5 | LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:69:18 + --> $DIR/manual_rem_euclid.rs:66:18 | LL | let _: i32 = ((x % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` error: manual `rem_euclid` implementation - --> $DIR/manual_rem_euclid.rs:84:18 + --> $DIR/manual_rem_euclid.rs:79:18 | LL | let _: i32 = ((x % 4) + 4) % 4; | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index fba503a20667..e5ae3cf3e503 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -1,5 +1,4 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_retain)] #![allow(unused)] use std::collections::BTreeMap; @@ -216,8 +215,8 @@ fn vec_deque_retain() { bar = foobar.into_iter().filter(|x| x % 2 == 0).collect(); } +#[clippy::msrv = "1.52"] fn _msrv_153() { - #![clippy::msrv = "1.52"] let mut btree_map: BTreeMap = (0..8).map(|x| (x, x * 10)).collect(); btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); @@ -225,14 +224,14 @@ fn _msrv_153() { btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); } +#[clippy::msrv = "1.25"] fn _msrv_126() { - #![clippy::msrv = "1.25"] let mut s = String::from("foobar"); s = s.chars().filter(|&c| c != 'o').to_owned().collect(); } +#[clippy::msrv = "1.17"] fn _msrv_118() { - #![clippy::msrv = "1.17"] let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]); hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); let mut hash_map: HashMap = (0..8).map(|x| (x, x * 10)).collect(); diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index 81a849fe7684..1021f15edd1e 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -1,5 +1,4 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_retain)] #![allow(unused)] use std::collections::BTreeMap; @@ -222,8 +221,8 @@ fn vec_deque_retain() { bar = foobar.into_iter().filter(|x| x % 2 == 0).collect(); } +#[clippy::msrv = "1.52"] fn _msrv_153() { - #![clippy::msrv = "1.52"] let mut btree_map: BTreeMap = (0..8).map(|x| (x, x * 10)).collect(); btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); @@ -231,14 +230,14 @@ fn _msrv_153() { btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); } +#[clippy::msrv = "1.25"] fn _msrv_126() { - #![clippy::msrv = "1.25"] let mut s = String::from("foobar"); s = s.chars().filter(|&c| c != 'o').to_owned().collect(); } +#[clippy::msrv = "1.17"] fn _msrv_118() { - #![clippy::msrv = "1.17"] let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]); hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); let mut hash_map: HashMap = (0..8).map(|x| (x, x * 10)).collect(); diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr index ec635919b48f..89316ce1d998 100644 --- a/tests/ui/manual_retain.stderr +++ b/tests/ui/manual_retain.stderr @@ -1,5 +1,5 @@ error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:52:5 + --> $DIR/manual_retain.rs:51:5 | LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)` @@ -7,13 +7,13 @@ LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect() = note: `-D clippy::manual-retain` implied by `-D warnings` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:53:5 + --> $DIR/manual_retain.rs:52:5 | LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:54:5 + --> $DIR/manual_retain.rs:53:5 | LL | / btree_map = btree_map LL | | .into_iter() @@ -22,37 +22,37 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:76:5 + --> $DIR/manual_retain.rs:75:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:77:5 + --> $DIR/manual_retain.rs:76:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:78:5 + --> $DIR/manual_retain.rs:77:5 | LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:108:5 + --> $DIR/manual_retain.rs:107:5 | LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:109:5 + --> $DIR/manual_retain.rs:108:5 | LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:110:5 + --> $DIR/manual_retain.rs:109:5 | LL | / hash_map = hash_map LL | | .into_iter() @@ -61,61 +61,61 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:131:5 + --> $DIR/manual_retain.rs:130:5 | LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:132:5 + --> $DIR/manual_retain.rs:131:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:133:5 + --> $DIR/manual_retain.rs:132:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:162:5 + --> $DIR/manual_retain.rs:161:5 | LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:174:5 + --> $DIR/manual_retain.rs:173:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:175:5 + --> $DIR/manual_retain.rs:174:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:176:5 + --> $DIR/manual_retain.rs:175:5 | LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:198:5 + --> $DIR/manual_retain.rs:197:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:199:5 + --> $DIR/manual_retain.rs:198:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:200:5 + --> $DIR/manual_retain.rs:199:5 | LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed index c7ca770434a3..50b02019cc27 100644 --- a/tests/ui/manual_split_once.fixed +++ b/tests/ui/manual_split_once.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] @@ -127,8 +126,8 @@ fn indirect() -> Option<()> { None } +#[clippy::msrv = "1.51"] fn _msrv_1_51() { - #![clippy::msrv = "1.51"] // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); @@ -137,8 +136,8 @@ fn _msrv_1_51() { let b = iter.next().unwrap(); } +#[clippy::msrv = "1.52"] fn _msrv_1_52() { - #![clippy::msrv = "1.52"] let _ = "key=value".split_once('=').unwrap().1; let (a, b) = "a.b.c".split_once('.').unwrap(); diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs index ee2848a251ee..e1e8b71a9def 100644 --- a/tests/ui/manual_split_once.rs +++ b/tests/ui/manual_split_once.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] @@ -127,8 +126,8 @@ fn indirect() -> Option<()> { None } +#[clippy::msrv = "1.51"] fn _msrv_1_51() { - #![clippy::msrv = "1.51"] // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); @@ -137,8 +136,8 @@ fn _msrv_1_51() { let b = iter.next().unwrap(); } +#[clippy::msrv = "1.52"] fn _msrv_1_52() { - #![clippy::msrv = "1.52"] let _ = "key=value".splitn(2, '=').nth(1).unwrap(); let mut iter = "a.b.c".splitn(2, '.'); diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index 2696694680ad..78da5a16cc52 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -1,5 +1,5 @@ error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:14:13 + --> $DIR/manual_split_once.rs:13:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` @@ -7,79 +7,79 @@ LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); = note: `-D clippy::manual-split-once` implied by `-D warnings` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:15:13 + --> $DIR/manual_split_once.rs:14:13 | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:16:18 + --> $DIR/manual_split_once.rs:15:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:19:13 + --> $DIR/manual_split_once.rs:18:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:22:13 + --> $DIR/manual_split_once.rs:21:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:25:13 + --> $DIR/manual_split_once.rs:24:13 | LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:28:17 + --> $DIR/manual_split_once.rs:27:17 | LL | let _ = s.splitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:29:17 + --> $DIR/manual_split_once.rs:28:17 | LL | let _ = s.splitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:30:17 + --> $DIR/manual_split_once.rs:29:17 | LL | let _ = s.rsplitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:31:17 + --> $DIR/manual_split_once.rs:30:17 | LL | let _ = s.rsplitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:39:13 + --> $DIR/manual_split_once.rs:38:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:40:18 + --> $DIR/manual_split_once.rs:39:18 | LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:41:13 + --> $DIR/manual_split_once.rs:40:13 | LL | let _ = s.rsplitn(2, '=').nth(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:45:5 + --> $DIR/manual_split_once.rs:44:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +104,7 @@ LL + | error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:49:5 + --> $DIR/manual_split_once.rs:48:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL + | error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:53:5 + --> $DIR/manual_split_once.rs:52:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL + | error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:57:5 + --> $DIR/manual_split_once.rs:56:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -179,13 +179,13 @@ LL + | error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:142:13 + --> $DIR/manual_split_once.rs:141:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:144:5 + --> $DIR/manual_split_once.rs:143:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed index 0704ba2f933e..3d56f2a0dedb 100644 --- a/tests/ui/manual_str_repeat.fixed +++ b/tests/ui/manual_str_repeat.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; @@ -54,13 +53,13 @@ fn main() { let _: String = repeat(x).take(count).collect(); } +#[clippy::msrv = "1.15"] fn _msrv_1_15() { - #![clippy::msrv = "1.15"] // `str::repeat` was stabilized in 1.16. Do not lint this let _: String = std::iter::repeat("test").take(10).collect(); } +#[clippy::msrv = "1.16"] fn _msrv_1_16() { - #![clippy::msrv = "1.16"] let _: String = "test".repeat(10); } diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs index f522be439aa0..e8240a949dbc 100644 --- a/tests/ui/manual_str_repeat.rs +++ b/tests/ui/manual_str_repeat.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; @@ -54,13 +53,13 @@ fn main() { let _: String = repeat(x).take(count).collect(); } +#[clippy::msrv = "1.15"] fn _msrv_1_15() { - #![clippy::msrv = "1.15"] // `str::repeat` was stabilized in 1.16. Do not lint this let _: String = std::iter::repeat("test").take(10).collect(); } +#[clippy::msrv = "1.16"] fn _msrv_1_16() { - #![clippy::msrv = "1.16"] let _: String = std::iter::repeat("test").take(10).collect(); } diff --git a/tests/ui/manual_str_repeat.stderr b/tests/ui/manual_str_repeat.stderr index c65116897164..bdfee7cab261 100644 --- a/tests/ui/manual_str_repeat.stderr +++ b/tests/ui/manual_str_repeat.stderr @@ -1,5 +1,5 @@ error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:10:21 + --> $DIR/manual_str_repeat.rs:9:21 | LL | let _: String = std::iter::repeat("test").take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` @@ -7,55 +7,55 @@ LL | let _: String = std::iter::repeat("test").take(10).collect(); = note: `-D clippy::manual-str-repeat` implied by `-D warnings` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:11:21 + --> $DIR/manual_str_repeat.rs:10:21 | LL | let _: String = std::iter::repeat('x').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:12:21 + --> $DIR/manual_str_repeat.rs:11:21 | LL | let _: String = std::iter::repeat('/'').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:13:21 + --> $DIR/manual_str_repeat.rs:12:21 | LL | let _: String = std::iter::repeat('"').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:17:13 + --> $DIR/manual_str_repeat.rs:16:13 | LL | let _ = repeat(x).take(count + 2).collect::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:26:21 + --> $DIR/manual_str_repeat.rs:25:21 | LL | let _: String = repeat(*x).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:35:21 + --> $DIR/manual_str_repeat.rs:34:21 | LL | let _: String = repeat(x).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:47:21 + --> $DIR/manual_str_repeat.rs:46:21 | LL | let _: String = repeat(Cow::Borrowed("test")).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Cow::Borrowed("test").repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:50:21 + --> $DIR/manual_str_repeat.rs:49:21 | LL | let _: String = repeat(x).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:65:21 + --> $DIR/manual_str_repeat.rs:64:21 | LL | let _: String = std::iter::repeat("test").take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index 85009d78558b..b0b1c262aeed 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -1,4 +1,3 @@ -#![feature(custom_inner_attributes)] #![warn(clippy::manual_strip)] fn main() { @@ -66,18 +65,16 @@ fn main() { } } +#[clippy::msrv = "1.44"] fn msrv_1_44() { - #![clippy::msrv = "1.44"] - let s = "abc"; if s.starts_with('a') { s[1..].to_string(); } } +#[clippy::msrv = "1.45"] fn msrv_1_45() { - #![clippy::msrv = "1.45"] - let s = "abc"; if s.starts_with('a') { s[1..].to_string(); diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index ad2a362f3e76..f592e898fc92 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -1,11 +1,11 @@ error: stripping a prefix manually - --> $DIR/manual_strip.rs:8:24 + --> $DIR/manual_strip.rs:7:24 | LL | str::to_string(&s["ab".len()..]); | ^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:7:5 + --> $DIR/manual_strip.rs:6:5 | LL | if s.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL ~ .to_string(); | error: stripping a suffix manually - --> $DIR/manual_strip.rs:16:24 + --> $DIR/manual_strip.rs:15:24 | LL | str::to_string(&s[..s.len() - "bc".len()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the suffix was tested here - --> $DIR/manual_strip.rs:15:5 + --> $DIR/manual_strip.rs:14:5 | LL | if s.ends_with("bc") { | ^^^^^^^^^^^^^^^^^^^^^ @@ -42,13 +42,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:25:24 + --> $DIR/manual_strip.rs:24:24 | LL | str::to_string(&s[1..]); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:24:5 + --> $DIR/manual_strip.rs:23:5 | LL | if s.starts_with('a') { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -60,13 +60,13 @@ LL ~ .to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:32:24 + --> $DIR/manual_strip.rs:31:24 | LL | str::to_string(&s[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:31:5 + --> $DIR/manual_strip.rs:30:5 | LL | if s.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,13 +77,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:38:24 + --> $DIR/manual_strip.rs:37:24 | LL | str::to_string(&s[PREFIX.len()..]); | ^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:37:5 + --> $DIR/manual_strip.rs:36:5 | LL | if s.starts_with(PREFIX) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,13 +95,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:45:24 + --> $DIR/manual_strip.rs:44:24 | LL | str::to_string(&TARGET[prefix.len()..]); | ^^^^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:44:5 + --> $DIR/manual_strip.rs:43:5 | LL | if TARGET.starts_with(prefix) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,13 +112,13 @@ LL ~ str::to_string(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:51:9 + --> $DIR/manual_strip.rs:50:9 | LL | s1[2..].to_uppercase(); | ^^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:50:5 + --> $DIR/manual_strip.rs:49:5 | LL | if s1.starts_with("ab") { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,13 +129,13 @@ LL ~ .to_uppercase(); | error: stripping a prefix manually - --> $DIR/manual_strip.rs:83:9 + --> $DIR/manual_strip.rs:80:9 | LL | s[1..].to_string(); | ^^^^^^ | note: the prefix was tested here - --> $DIR/manual_strip.rs:82:5 + --> $DIR/manual_strip.rs:79:5 | LL | if s.starts_with('a') { | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 396b22a9abb3..32631024ca5d 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,5 @@ // aux-build:option_helpers.rs -#![feature(custom_inner_attributes)] #![warn(clippy::map_unwrap_or)] #![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)] @@ -82,17 +81,15 @@ fn main() { result_methods(); } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - let res: Result = Ok(1); let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); } +#[clippy::msrv = "1.41"] fn msrv_1_41() { - #![clippy::msrv = "1.41"] - let res: Result = Ok(1); let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index d17d24a403ea..41781b050fa2 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,5 +1,5 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:18:13 + --> $DIR/map_unwrap_or.rs:17:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -15,7 +15,7 @@ LL + let _ = opt.map_or(0, |x| x + 1); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:22:13 + --> $DIR/map_unwrap_or.rs:21:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -33,7 +33,7 @@ LL ~ ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:26:13 + --> $DIR/map_unwrap_or.rs:25:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -50,7 +50,7 @@ LL ~ }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:31:13 + --> $DIR/map_unwrap_or.rs:30:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + let _ = opt.and_then(|x| Some(x + 1)); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:33:13 + --> $DIR/map_unwrap_or.rs:32:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -80,7 +80,7 @@ LL ~ ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:37:13 + --> $DIR/map_unwrap_or.rs:36:13 | LL | let _ = opt | _____________^ @@ -95,7 +95,7 @@ LL + .and_then(|x| Some(x + 1)); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:48:13 + --> $DIR/map_unwrap_or.rs:47:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:52:13 + --> $DIR/map_unwrap_or.rs:51:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -117,7 +117,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:56:13 + --> $DIR/map_unwrap_or.rs:55:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -127,7 +127,7 @@ LL | | ); | |_________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:68:13 + --> $DIR/map_unwrap_or.rs:67:13 | LL | let _ = res.map(|x| { | _____________^ @@ -137,7 +137,7 @@ LL | | ).unwrap_or_else(|_e| 0); | |____________________________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:72:13 + --> $DIR/map_unwrap_or.rs:71:13 | LL | let _ = res.map(|x| x + 1) | _____________^ @@ -147,7 +147,7 @@ LL | | }); | |__________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:98:13 + --> $DIR/map_unwrap_or.rs:95:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 968f462f8a02..55cd15bd5c38 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, @@ -200,17 +199,15 @@ fn main() { }; } +#[clippy::msrv = "1.41"] fn msrv_1_41() { - #![clippy::msrv = "1.41"] - let _y = match Some(5) { Some(0) => true, _ => false, }; } +#[clippy::msrv = "1.42"] fn msrv_1_42() { - #![clippy::msrv = "1.42"] - let _y = matches!(Some(5), Some(0)); } diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index c6b479e27c5a..5d645e108e51 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::match_like_matches_macro)] #![allow( unreachable_patterns, @@ -241,18 +240,16 @@ fn main() { }; } +#[clippy::msrv = "1.41"] fn msrv_1_41() { - #![clippy::msrv = "1.41"] - let _y = match Some(5) { Some(0) => true, _ => false, }; } +#[clippy::msrv = "1.42"] fn msrv_1_42() { - #![clippy::msrv = "1.42"] - let _y = match Some(5) { Some(0) => true, _ => false, diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index a4df8008ac23..46f67ef4900f 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:16:14 + --> $DIR/match_expr_like_matches_macro.rs:15:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:22:14 + --> $DIR/match_expr_like_matches_macro.rs:21:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:28:14 + --> $DIR/match_expr_like_matches_macro.rs:27:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:34:15 + --> $DIR/match_expr_like_matches_macro.rs:33:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:40:16 + --> $DIR/match_expr_like_matches_macro.rs:39:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:64:20 + --> $DIR/match_expr_like_matches_macro.rs:63:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:74:20 + --> $DIR/match_expr_like_matches_macro.rs:73:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:84:20 + --> $DIR/match_expr_like_matches_macro.rs:83:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:144:18 + --> $DIR/match_expr_like_matches_macro.rs:143:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:153:18 + --> $DIR/match_expr_like_matches_macro.rs:152:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:170:21 + --> $DIR/match_expr_like_matches_macro.rs:169:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:184:20 + --> $DIR/match_expr_like_matches_macro.rs:183:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:196:20 + --> $DIR/match_expr_like_matches_macro.rs:195:20 | LL | let _res = match &val { | ____________________^ @@ -134,7 +134,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:256:14 + --> $DIR/match_expr_like_matches_macro.rs:253:14 | LL | let _y = match Some(5) { | ______________^ diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index ae237395b95f..874d55843303 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] #![warn( clippy::all, @@ -80,16 +79,14 @@ fn main() { dont_lint_primitive(); } +#[clippy::msrv = "1.39"] fn msrv_1_39() { - #![clippy::msrv = "1.39"] - let mut s = String::from("foo"); let _ = std::mem::replace(&mut s, String::default()); } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - let mut s = String::from("foo"); let _ = std::mem::take(&mut s); } diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 3202e99e0be9..f4f3bff51446 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] #![warn( clippy::all, @@ -80,16 +79,14 @@ fn main() { dont_lint_primitive(); } +#[clippy::msrv = "1.39"] fn msrv_1_39() { - #![clippy::msrv = "1.39"] - let mut s = String::from("foo"); let _ = std::mem::replace(&mut s, String::default()); } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - let mut s = String::from("foo"); let _ = std::mem::replace(&mut s, String::default()); } diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index dd8a50dab900..caa127f76eef 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:17:13 + --> $DIR/mem_replace.rs:16:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:19:13 + --> $DIR/mem_replace.rs:18:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:24:13 + --> $DIR/mem_replace.rs:23:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,103 +21,103 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:27:13 + --> $DIR/mem_replace.rs:26:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:28:13 + --> $DIR/mem_replace.rs:27:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:31:13 + --> $DIR/mem_replace.rs:30:13 | LL | let _ = std::mem::replace(&mut v, Vec::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:32:13 + --> $DIR/mem_replace.rs:31:13 | LL | let _ = std::mem::replace(&mut v, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:33:13 + --> $DIR/mem_replace.rs:32:13 | LL | let _ = std::mem::replace(&mut v, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:34:13 + --> $DIR/mem_replace.rs:33:13 | LL | let _ = std::mem::replace(&mut v, vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:37:13 + --> $DIR/mem_replace.rs:36:13 | LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:40:13 + --> $DIR/mem_replace.rs:39:13 | LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:43:13 + --> $DIR/mem_replace.rs:42:13 | LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:46:13 + --> $DIR/mem_replace.rs:45:13 | LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:49:13 + --> $DIR/mem_replace.rs:48:13 | LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:52:13 + --> $DIR/mem_replace.rs:51:13 | LL | let _ = std::mem::replace(&mut list, LinkedList::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:55:13 + --> $DIR/mem_replace.rs:54:13 | LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:58:13 + --> $DIR/mem_replace.rs:57:13 | LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:61:13 + --> $DIR/mem_replace.rs:60:13 | LL | let _ = std::mem::replace(&mut refstr, ""); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:64:13 + --> $DIR/mem_replace.rs:63:13 | LL | let _ = std::mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:94:13 + --> $DIR/mem_replace.rs:91:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index cd148063bf06..955e7eb72763 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -3,27 +3,60 @@ fn main() {} +#[clippy::msrv = "1.42.0"] fn just_under_msrv() { - #![clippy::msrv = "1.42.0"] let log2_10 = 3.321928094887362; } +#[clippy::msrv = "1.43.0"] fn meets_msrv() { - #![clippy::msrv = "1.43.0"] let log2_10 = 3.321928094887362; } +#[clippy::msrv = "1.44.0"] fn just_above_msrv() { - #![clippy::msrv = "1.44.0"] let log2_10 = 3.321928094887362; } +#[clippy::msrv = "1.42"] fn no_patch_under() { - #![clippy::msrv = "1.42"] let log2_10 = 3.321928094887362; } +#[clippy::msrv = "1.43"] fn no_patch_meets() { + let log2_10 = 3.321928094887362; +} + +fn inner_attr_under() { + #![clippy::msrv = "1.42"] + let log2_10 = 3.321928094887362; +} + +fn inner_attr_meets() { #![clippy::msrv = "1.43"] let log2_10 = 3.321928094887362; } + +// https://github.com/rust-lang/rust-clippy/issues/6920 +fn scoping() { + mod m { + #![clippy::msrv = "1.42.0"] + } + + // Should warn + let log2_10 = 3.321928094887362; + + mod a { + #![clippy::msrv = "1.42.0"] + + fn should_warn() { + #![clippy::msrv = "1.43.0"] + let log2_10 = 3.321928094887362; + } + + fn should_not_warn() { + let log2_10 = 3.321928094887362; + } + } +} diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 68aa58748190..7e2135584efd 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -23,5 +23,29 @@ LL | let log2_10 = 3.321928094887362; | = help: consider using the constant directly -error: aborting due to 3 previous errors +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:38:19 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:48:19 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:55:27 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly + +error: aborting due to 6 previous errors diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr index 93370a0fa9c9..675b78031525 100644 --- a/tests/ui/min_rust_version_invalid_attr.stderr +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -4,7 +4,7 @@ error: `invalid.version` is not a valid Rust version LL | #![clippy::msrv = "invalid.version"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `msrv` cannot be an outer attribute +error: `invalid.version` is not a valid Rust version --> $DIR/min_rust_version_invalid_attr.rs:6:1 | LL | #[clippy::msrv = "invalid.version"] diff --git a/tests/ui/misnamed_getters.rs b/tests/ui/misnamed_getters.rs new file mode 100644 index 000000000000..03e7dac7df94 --- /dev/null +++ b/tests/ui/misnamed_getters.rs @@ -0,0 +1,124 @@ +#![allow(unused)] +#![warn(clippy::misnamed_getters)] + +struct A { + a: u8, + b: u8, + c: u8, +} + +impl A { + fn a(&self) -> &u8 { + &self.b + } + fn a_mut(&mut self) -> &mut u8 { + &mut self.b + } + + fn b(self) -> u8 { + self.a + } + + fn b_mut(&mut self) -> &mut u8 { + &mut self.a + } + + fn c(&self) -> &u8 { + &self.b + } + + fn c_mut(&mut self) -> &mut u8 { + &mut self.a + } +} + +union B { + a: u8, + b: u8, +} + +impl B { + unsafe fn a(&self) -> &u8 { + &self.b + } + unsafe fn a_mut(&mut self) -> &mut u8 { + &mut self.b + } + + unsafe fn b(self) -> u8 { + self.a + } + + unsafe fn b_mut(&mut self) -> &mut u8 { + &mut self.a + } + + unsafe fn c(&self) -> &u8 { + &self.b + } + + unsafe fn c_mut(&mut self) -> &mut u8 { + &mut self.a + } + + unsafe fn a_unchecked(&self) -> &u8 { + &self.b + } + unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { + &mut self.b + } + + unsafe fn b_unchecked(self) -> u8 { + self.a + } + + unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { + &mut self.a + } + + unsafe fn c_unchecked(&self) -> &u8 { + &self.b + } + + unsafe fn c_unchecked_mut(&mut self) -> &mut u8 { + &mut self.a + } +} + +struct D { + d: u8, + inner: A, +} + +impl core::ops::Deref for D { + type Target = A; + fn deref(&self) -> &A { + &self.inner + } +} + +impl core::ops::DerefMut for D { + fn deref_mut(&mut self) -> &mut A { + &mut self.inner + } +} + +impl D { + fn a(&self) -> &u8 { + &self.b + } + fn a_mut(&mut self) -> &mut u8 { + &mut self.b + } + + fn d(&self) -> &u8 { + &self.b + } + fn d_mut(&mut self) -> &mut u8 { + &mut self.b + } +} + +fn main() { + // test code goes here +} diff --git a/tests/ui/misnamed_getters.stderr b/tests/ui/misnamed_getters.stderr new file mode 100644 index 000000000000..1e38a83d019a --- /dev/null +++ b/tests/ui/misnamed_getters.stderr @@ -0,0 +1,166 @@ +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:11:5 + | +LL | / fn a(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + | + = note: `-D clippy::misnamed-getters` implied by `-D warnings` + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:14:5 + | +LL | / fn a_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:18:5 + | +LL | / fn b(self) -> u8 { +LL | | self.a + | | ------ help: consider using: `self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:22:5 + | +LL | / fn b_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:26:5 + | +LL | / fn c(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.c` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:30:5 + | +LL | / fn c_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.c` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:41:5 + | +LL | / unsafe fn a(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:44:5 + | +LL | / unsafe fn a_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:48:5 + | +LL | / unsafe fn b(self) -> u8 { +LL | | self.a + | | ------ help: consider using: `self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:52:5 + | +LL | / unsafe fn b_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:64:5 + | +LL | / unsafe fn a_unchecked(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:67:5 + | +LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:71:5 + | +LL | / unsafe fn b_unchecked(self) -> u8 { +LL | | self.a + | | ------ help: consider using: `self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:75:5 + | +LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:107:5 + | +LL | / fn a(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:110:5 + | +LL | / fn a_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:114:5 + | +LL | / fn d(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.d` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:117:5 + | +LL | / fn d_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.d` +LL | | } + | |_____^ + +error: aborting due to 18 previous errors + diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index b950248ef942..75cace181675 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -7,7 +7,6 @@ #![warn(clippy::missing_const_for_fn)] #![feature(start)] -#![feature(custom_inner_attributes)] extern crate helper; extern crate proc_macro_with_span; @@ -115,9 +114,8 @@ fn unstably_const_fn() { helper::unstably_const_fn() } +#[clippy::msrv = "1.46.0"] mod const_fn_stabilized_after_msrv { - #![clippy::msrv = "1.46.0"] - // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0. fn const_fn_stabilized_after_msrv(byte: u8) { byte.is_ascii_digit(); diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index b85e88784918..0246c8622ed3 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,6 +1,5 @@ #![warn(clippy::missing_const_for_fn)] #![allow(incomplete_features, clippy::let_and_return)] -#![feature(custom_inner_attributes)] use std::mem::transmute; @@ -68,24 +67,21 @@ mod with_drop { } } +#[clippy::msrv = "1.47.0"] mod const_fn_stabilized_before_msrv { - #![clippy::msrv = "1.47.0"] - // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47. fn const_fn_stabilized_before_msrv(byte: u8) { byte.is_ascii_digit(); } } +#[clippy::msrv = "1.45"] fn msrv_1_45() -> i32 { - #![clippy::msrv = "1.45"] - 45 } +#[clippy::msrv = "1.46"] fn msrv_1_46() -> i32 { - #![clippy::msrv = "1.46"] - 46 } diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index f8e221c82f1a..955e1ed26340 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -1,5 +1,5 @@ error: this could be a `const fn` - --> $DIR/could_be_const.rs:13:5 + --> $DIR/could_be_const.rs:12:5 | LL | / pub fn new() -> Self { LL | | Self { guess: 42 } @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` error: this could be a `const fn` - --> $DIR/could_be_const.rs:17:5 + --> $DIR/could_be_const.rs:16:5 | LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { LL | | b @@ -17,7 +17,7 @@ LL | | } | |_____^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:23:1 + --> $DIR/could_be_const.rs:22:1 | LL | / fn one() -> i32 { LL | | 1 @@ -25,7 +25,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:28:1 + --> $DIR/could_be_const.rs:27:1 | LL | / fn two() -> i32 { LL | | let abc = 2; @@ -34,7 +34,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:34:1 + --> $DIR/could_be_const.rs:33:1 | LL | / fn string() -> String { LL | | String::new() @@ -42,7 +42,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:39:1 + --> $DIR/could_be_const.rs:38:1 | LL | / unsafe fn four() -> i32 { LL | | 4 @@ -50,7 +50,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:44:1 + --> $DIR/could_be_const.rs:43:1 | LL | / fn generic(t: T) -> T { LL | | t @@ -58,7 +58,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:52:1 + --> $DIR/could_be_const.rs:51:1 | LL | / fn generic_arr(t: [T; 1]) -> T { LL | | t[0] @@ -66,7 +66,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:65:9 + --> $DIR/could_be_const.rs:64:9 | LL | / pub fn b(self, a: &A) -> B { LL | | B @@ -74,7 +74,7 @@ LL | | } | |_________^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:75:5 + --> $DIR/could_be_const.rs:73:5 | LL | / fn const_fn_stabilized_before_msrv(byte: u8) { LL | | byte.is_ascii_digit(); @@ -82,11 +82,9 @@ LL | | } | |_____^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:86:1 + --> $DIR/could_be_const.rs:84:1 | LL | / fn msrv_1_46() -> i32 { -LL | | #![clippy::msrv = "1.46"] -LL | | LL | | 46 LL | | } | |_^ diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 85b6b639d554..4cb7f6b687f1 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,13 +1,13 @@ // run-rustfix -#![feature(custom_inner_attributes, lint_reasons)] - -#[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables)] -#[allow( +#![feature(lint_reasons)] +#![allow( + unused, clippy::uninlined_format_args, clippy::unnecessary_mut_passed, clippy::unnecessary_to_owned )] +#![warn(clippy::needless_borrow)] + fn main() { let a = 5; let ref_a = &a; @@ -171,14 +171,12 @@ impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} -#[allow(dead_code)] fn check_expect_suppression() { let a = 5; #[expect(clippy::needless_borrow)] let _ = x(&&a); } -#[allow(dead_code)] mod issue9160 { pub struct S { f: F, @@ -267,7 +265,6 @@ where } // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -#[allow(dead_code)] mod copyable_iterator { #[derive(Clone, Copy)] struct Iter; @@ -287,25 +284,20 @@ mod copyable_iterator { } } +#[clippy::msrv = "1.52.0"] mod under_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.52.0"] - fn foo() { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } +#[clippy::msrv = "1.53.0"] mod meets_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.53.0"] - fn foo() { let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); } } -#[allow(unused)] fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -334,7 +326,6 @@ fn issue9383() { } } -#[allow(dead_code)] fn closure_test() { let env = "env".to_owned(); let arg = "arg".to_owned(); @@ -348,7 +339,6 @@ fn closure_test() { f(arg); } -#[allow(dead_code)] mod significant_drop { #[derive(Debug)] struct X; @@ -368,7 +358,6 @@ mod significant_drop { fn debug(_: impl std::fmt::Debug) {} } -#[allow(dead_code)] mod used_exactly_once { fn foo(x: String) { use_x(x); @@ -376,7 +365,6 @@ mod used_exactly_once { fn use_x(_: impl AsRef) {} } -#[allow(dead_code)] mod used_more_than_once { fn foo(x: String) { use_x(&x); @@ -387,7 +375,6 @@ mod used_more_than_once { } // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 -#[allow(dead_code)] mod issue_9111 { struct A; @@ -409,7 +396,6 @@ mod issue_9111 { } } -#[allow(dead_code)] mod issue_9710 { fn main() { let string = String::new(); @@ -421,7 +407,6 @@ mod issue_9710 { fn f>(_: T) {} } -#[allow(dead_code)] mod issue_9739 { fn foo(_it: impl IntoIterator) {} @@ -434,7 +419,6 @@ mod issue_9739 { } } -#[allow(dead_code)] mod issue_9739_method_variant { struct S; @@ -451,7 +435,6 @@ mod issue_9739_method_variant { } } -#[allow(dead_code)] mod issue_9782 { fn foo>(t: T) { println!("{}", std::mem::size_of::()); @@ -475,7 +458,6 @@ mod issue_9782 { } } -#[allow(dead_code)] mod issue_9782_type_relative_variant { struct S; @@ -493,7 +475,6 @@ mod issue_9782_type_relative_variant { } } -#[allow(dead_code)] mod issue_9782_method_variant { struct S; diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 7b97bcf3817e..9a01190ed8db 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,13 +1,13 @@ // run-rustfix -#![feature(custom_inner_attributes, lint_reasons)] - -#[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables)] -#[allow( +#![feature(lint_reasons)] +#![allow( + unused, clippy::uninlined_format_args, clippy::unnecessary_mut_passed, clippy::unnecessary_to_owned )] +#![warn(clippy::needless_borrow)] + fn main() { let a = 5; let ref_a = &a; @@ -171,14 +171,12 @@ impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} -#[allow(dead_code)] fn check_expect_suppression() { let a = 5; #[expect(clippy::needless_borrow)] let _ = x(&&a); } -#[allow(dead_code)] mod issue9160 { pub struct S { f: F, @@ -267,7 +265,6 @@ where } // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -#[allow(dead_code)] mod copyable_iterator { #[derive(Clone, Copy)] struct Iter; @@ -287,25 +284,20 @@ mod copyable_iterator { } } +#[clippy::msrv = "1.52.0"] mod under_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.52.0"] - fn foo() { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } +#[clippy::msrv = "1.53.0"] mod meets_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.53.0"] - fn foo() { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } -#[allow(unused)] fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -334,7 +326,6 @@ fn issue9383() { } } -#[allow(dead_code)] fn closure_test() { let env = "env".to_owned(); let arg = "arg".to_owned(); @@ -348,7 +339,6 @@ fn closure_test() { f(arg); } -#[allow(dead_code)] mod significant_drop { #[derive(Debug)] struct X; @@ -368,7 +358,6 @@ mod significant_drop { fn debug(_: impl std::fmt::Debug) {} } -#[allow(dead_code)] mod used_exactly_once { fn foo(x: String) { use_x(&x); @@ -376,7 +365,6 @@ mod used_exactly_once { fn use_x(_: impl AsRef) {} } -#[allow(dead_code)] mod used_more_than_once { fn foo(x: String) { use_x(&x); @@ -387,7 +375,6 @@ mod used_more_than_once { } // https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 -#[allow(dead_code)] mod issue_9111 { struct A; @@ -409,7 +396,6 @@ mod issue_9111 { } } -#[allow(dead_code)] mod issue_9710 { fn main() { let string = String::new(); @@ -421,7 +407,6 @@ mod issue_9710 { fn f>(_: T) {} } -#[allow(dead_code)] mod issue_9739 { fn foo(_it: impl IntoIterator) {} @@ -434,7 +419,6 @@ mod issue_9739 { } } -#[allow(dead_code)] mod issue_9739_method_variant { struct S; @@ -451,7 +435,6 @@ mod issue_9739_method_variant { } } -#[allow(dead_code)] mod issue_9782 { fn foo>(t: T) { println!("{}", std::mem::size_of::()); @@ -475,7 +458,6 @@ mod issue_9782 { } } -#[allow(dead_code)] mod issue_9782_type_relative_variant { struct S; @@ -493,7 +475,6 @@ mod issue_9782_type_relative_variant { } } -#[allow(dead_code)] mod issue_9782_method_variant { struct S; diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 485e6b84c868..d26c317124b8 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -163,55 +163,55 @@ LL | let _ = std::fs::write("x", &"".to_string()); | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:192:13 + --> $DIR/needless_borrow.rs:190:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:201:13 + --> $DIR/needless_borrow.rs:199:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:286:20 + --> $DIR/needless_borrow.rs:283:20 | LL | takes_iter(&mut x) | ^^^^^^ help: change this to: `x` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:304:55 + --> $DIR/needless_borrow.rs:297:55 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:344:37 + --> $DIR/needless_borrow.rs:335:37 | LL | let _ = std::fs::write("x", &arg); | ^^^^ help: change this to: `arg` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:345:37 + --> $DIR/needless_borrow.rs:336:37 | LL | let _ = std::fs::write("x", &loc); | ^^^^ help: change this to: `loc` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:364:15 + --> $DIR/needless_borrow.rs:354:15 | LL | debug(&x); | ^^ help: change this to: `x` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:374:15 + --> $DIR/needless_borrow.rs:363:15 | LL | use_x(&x); | ^^ help: change this to: `x` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:474:13 + --> $DIR/needless_borrow.rs:457:13 | LL | foo(&a); | ^^ help: change this to: `a` diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index ba9d15e59d0e..7eaca571992f 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -8,7 +8,6 @@ dead_code, unused_must_use )] -#![feature(custom_inner_attributes)] struct TO { magic: Option, diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 3a6523e8fe87..960bc7b78983 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -8,7 +8,6 @@ dead_code, unused_must_use )] -#![feature(custom_inner_attributes)] struct TO { magic: Option, diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index f8308e24e771..d1f89e326c67 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -1,5 +1,5 @@ error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:23:12 + --> $DIR/needless_question_mark.rs:22:12 | LL | return Some(to.magic?); | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` @@ -7,67 +7,67 @@ LL | return Some(to.magic?); = note: `-D clippy::needless-question-mark` implied by `-D warnings` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:31:12 + --> $DIR/needless_question_mark.rs:30:12 | LL | return Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:36:5 + --> $DIR/needless_question_mark.rs:35:5 | LL | Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:41:21 + --> $DIR/needless_question_mark.rs:40:21 | LL | to.and_then(|t| Some(t.magic?)) | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:50:9 + --> $DIR/needless_question_mark.rs:49:9 | LL | Some(t.magic?) | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:55:12 + --> $DIR/needless_question_mark.rs:54:12 | LL | return Ok(tr.magic?); | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:62:12 + --> $DIR/needless_question_mark.rs:61:12 | LL | return Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:66:5 + --> $DIR/needless_question_mark.rs:65:5 | LL | Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:70:21 + --> $DIR/needless_question_mark.rs:69:21 | LL | tr.and_then(|t| Ok(t.magic?)) | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:78:9 + --> $DIR/needless_question_mark.rs:77:9 | LL | Ok(t.magic?) | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:85:16 + --> $DIR/needless_question_mark.rs:84:16 | LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:120:27 + --> $DIR/needless_question_mark.rs:119:27 | LL | || -> Option<_> { Some(Some($expr)?) }() | ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)` @@ -78,13 +78,13 @@ LL | let _x = some_and_qmark_in_macro!(x?); = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:131:5 + --> $DIR/needless_question_mark.rs:130:5 | LL | Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:139:5 + --> $DIR/needless_question_mark.rs:138:5 | LL | Ok(s.magic?) | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic` diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d2163b14fcad..4386aaec49e2 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -59,14 +59,11 @@ fn test_macro_call() -> i32 { } fn test_void_fun() { - } fn test_void_if_fun(b: bool) { if b { - } else { - } } @@ -82,7 +79,6 @@ fn test_nested_match(x: u32) { 0 => (), 1 => { let _ = 42; - }, _ => (), } @@ -126,7 +122,6 @@ mod issue6501 { fn test_closure() { let _ = || { - }; let _ = || {}; } @@ -179,14 +174,11 @@ async fn async_test_macro_call() -> i32 { } async fn async_test_void_fun() { - } async fn async_test_void_if_fun(b: bool) { if b { - } else { - } } @@ -269,4 +261,15 @@ fn issue9503(x: usize) -> isize { } } +mod issue9416 { + pub fn with_newline() { + let _ = 42; + } + + #[rustfmt::skip] + pub fn oneline() { + let _ = 42; + } +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 114414b5fac7..666dc54b76b4 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -269,4 +269,17 @@ fn issue9503(x: usize) -> isize { }; } +mod issue9416 { + pub fn with_newline() { + let _ = 42; + + return; + } + + #[rustfmt::skip] + pub fn oneline() { + let _ = 42; return; + } +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 047fb6c2311a..a8b5d86cd558 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -72,26 +72,32 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:62:5 + --> $DIR/needless_return.rs:61:21 | -LL | return; - | ^^^^^^ +LL | fn test_void_fun() { + | _____________________^ +LL | | return; + | |__________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:67:9 + --> $DIR/needless_return.rs:66:11 | -LL | return; - | ^^^^^^ +LL | if b { + | ___________^ +LL | | return; + | |______________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:69:9 + --> $DIR/needless_return.rs:68:13 | -LL | return; - | ^^^^^^ +LL | } else { + | _____________^ +LL | | return; + | |______________^ | = help: remove `return` @@ -104,10 +110,12 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:85:13 + --> $DIR/needless_return.rs:84:24 | -LL | return; - | ^^^^^^ +LL | let _ = 42; + | ________________________^ +LL | | return; + | |__________________^ | = help: remove `return` @@ -144,10 +152,12 @@ LL | bar.unwrap_or_else(|_| return) = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:129:13 + --> $DIR/needless_return.rs:128:21 | -LL | return; - | ^^^^^^ +LL | let _ = || { + | _____________________^ +LL | | return; + | |__________________^ | = help: remove `return` @@ -240,26 +250,32 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:182:5 + --> $DIR/needless_return.rs:181:33 | -LL | return; - | ^^^^^^ +LL | async fn async_test_void_fun() { + | _________________________________^ +LL | | return; + | |__________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:187:9 + --> $DIR/needless_return.rs:186:11 | -LL | return; - | ^^^^^^ +LL | if b { + | ___________^ +LL | | return; + | |______________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:189:9 + --> $DIR/needless_return.rs:188:13 | -LL | return; - | ^^^^^^ +LL | } else { + | _____________^ +LL | | return; + | |______________^ | = help: remove `return` @@ -351,5 +367,24 @@ LL | return !*(x as *const isize); | = help: remove `return` -error: aborting due to 44 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:274:20 + | +LL | let _ = 42; + | ____________________^ +LL | | +LL | | return; + | |______________^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:281:20 + | +LL | let _ = 42; return; + | ^^^^^^^ + | + = help: remove `return` + +error: aborting due to 46 previous errors diff --git a/tests/ui/needless_splitn.fixed b/tests/ui/needless_splitn.fixed index 61f5fc4e679e..5496031fefab 100644 --- a/tests/ui/needless_splitn.fixed +++ b/tests/ui/needless_splitn.fixed @@ -1,7 +1,6 @@ // run-rustfix // edition:2018 -#![feature(custom_inner_attributes)] #![warn(clippy::needless_splitn)] #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)] @@ -40,8 +39,8 @@ fn _question_mark(s: &str) -> Option<()> { Some(()) } +#[clippy::msrv = "1.51"] fn _test_msrv() { - #![clippy::msrv = "1.51"] // `manual_split_once` MSRV shouldn't apply to `needless_splitn` let _ = "key=value".split('=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.rs b/tests/ui/needless_splitn.rs index 71d9a7077faa..35c2465bae13 100644 --- a/tests/ui/needless_splitn.rs +++ b/tests/ui/needless_splitn.rs @@ -1,7 +1,6 @@ // run-rustfix // edition:2018 -#![feature(custom_inner_attributes)] #![warn(clippy::needless_splitn)] #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)] @@ -40,8 +39,8 @@ fn _question_mark(s: &str) -> Option<()> { Some(()) } +#[clippy::msrv = "1.51"] fn _test_msrv() { - #![clippy::msrv = "1.51"] // `manual_split_once` MSRV shouldn't apply to `needless_splitn` let _ = "key=value".splitn(2, '=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.stderr b/tests/ui/needless_splitn.stderr index f112b29e7f20..f607d8e1ab5f 100644 --- a/tests/ui/needless_splitn.stderr +++ b/tests/ui/needless_splitn.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:15:13 + --> $DIR/needless_splitn.rs:14:13 | LL | let _ = str.splitn(2, '=').next(); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` @@ -7,73 +7,73 @@ LL | let _ = str.splitn(2, '=').next(); = note: `-D clippy::needless-splitn` implied by `-D warnings` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:16:13 + --> $DIR/needless_splitn.rs:15:13 | LL | let _ = str.splitn(2, '=').nth(0); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:19:18 + --> $DIR/needless_splitn.rs:18:18 | LL | let (_, _) = str.splitn(3, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:22:13 + --> $DIR/needless_splitn.rs:21:13 | LL | let _ = str.rsplitn(2, '=').next(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:23:13 + --> $DIR/needless_splitn.rs:22:13 | LL | let _ = str.rsplitn(2, '=').nth(0); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:26:18 + --> $DIR/needless_splitn.rs:25:18 | LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:28:13 + --> $DIR/needless_splitn.rs:27:13 | LL | let _ = str.splitn(5, '=').next(); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:29:13 + --> $DIR/needless_splitn.rs:28:13 | LL | let _ = str.splitn(5, '=').nth(3); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:35:13 + --> $DIR/needless_splitn.rs:34:13 | LL | let _ = s.splitn(2, '=').next()?; | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:36:13 + --> $DIR/needless_splitn.rs:35:13 | LL | let _ = s.splitn(2, '=').nth(0)?; | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:37:13 + --> $DIR/needless_splitn.rs:36:13 | LL | let _ = s.rsplitn(2, '=').next()?; | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:38:13 + --> $DIR/needless_splitn.rs:37:13 | LL | let _ = s.rsplitn(2, '=').nth(0)?; | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:46:13 + --> $DIR/needless_splitn.rs:45:13 | LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')` diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index bc376d0d7fb3..d124d133faa2 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] @@ -44,16 +43,14 @@ fn main() { let _ = opt.as_deref(); } +#[clippy::msrv = "1.39"] fn msrv_1_39() { - #![clippy::msrv = "1.39"] - let opt = Some(String::from("123")); let _ = opt.as_ref().map(String::as_str); } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - let opt = Some(String::from("123")); let _ = opt.as_deref(); } diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index ba3a2eedc225..86e354c6716b 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] @@ -47,16 +46,14 @@ fn main() { let _ = opt.as_ref().map(std::ops::Deref::deref); } +#[clippy::msrv = "1.39"] fn msrv_1_39() { - #![clippy::msrv = "1.39"] - let opt = Some(String::from("123")); let _ = opt.as_ref().map(String::as_str); } +#[clippy::msrv = "1.40"] fn msrv_1_40() { - #![clippy::msrv = "1.40"] - let opt = Some(String::from("123")); let _ = opt.as_ref().map(String::as_str); } diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index 7de8b3b6ba43..e471b56eea81 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -1,5 +1,5 @@ error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:14:13 + --> $DIR/option_as_ref_deref.rs:13:13 | LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` @@ -7,7 +7,7 @@ LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead - --> $DIR/option_as_ref_deref.rs:17:13 + --> $DIR/option_as_ref_deref.rs:16:13 | LL | let _ = opt.clone() | _____________^ @@ -17,97 +17,97 @@ LL | | ) | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:23:13 + --> $DIR/option_as_ref_deref.rs:22:13 | LL | let _ = opt.as_mut().map(DerefMut::deref_mut); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:25:13 + --> $DIR/option_as_ref_deref.rs:24:13 | LL | let _ = opt.as_ref().map(String::as_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:26:13 + --> $DIR/option_as_ref_deref.rs:25:13 | LL | let _ = opt.as_ref().map(|x| x.as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:27:13 + --> $DIR/option_as_ref_deref.rs:26:13 | LL | let _ = opt.as_mut().map(String::as_mut_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:28:13 + --> $DIR/option_as_ref_deref.rs:27:13 | LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:29:13 + --> $DIR/option_as_ref_deref.rs:28:13 | LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:30:13 + --> $DIR/option_as_ref_deref.rs:29:13 | LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:31:13 + --> $DIR/option_as_ref_deref.rs:30:13 | LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead - --> $DIR/option_as_ref_deref.rs:32:13 + --> $DIR/option_as_ref_deref.rs:31:13 | LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:33:13 + --> $DIR/option_as_ref_deref.rs:32:13 | LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:35:13 + --> $DIR/option_as_ref_deref.rs:34:13 | LL | let _ = opt.as_ref().map(|x| x.deref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:36:13 + --> $DIR/option_as_ref_deref.rs:35:13 | LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:43:13 + --> $DIR/option_as_ref_deref.rs:42:13 | LL | let _ = opt.as_ref().map(|x| &**x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead - --> $DIR/option_as_ref_deref.rs:44:13 + --> $DIR/option_as_ref_deref.rs:43:13 | LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:47:13 + --> $DIR/option_as_ref_deref.rs:46:13 | LL | let _ = opt.as_ref().map(std::ops::Deref::deref); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead - --> $DIR/option_as_ref_deref.rs:61:13 + --> $DIR/option_as_ref_deref.rs:58:13 | LL | let _ = opt.as_ref().map(String::as_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed index bea6be66a8e0..df36a9b842bf 100644 --- a/tests/ui/ptr_as_ptr.fixed +++ b/tests/ui/ptr_as_ptr.fixed @@ -2,7 +2,6 @@ // aux-build:macro_rules.rs #![warn(clippy::ptr_as_ptr)] -#![feature(custom_inner_attributes)] extern crate macro_rules; @@ -45,8 +44,8 @@ fn main() { let _ = macro_rules::ptr_as_ptr_cast!(ptr); } +#[clippy::msrv = "1.37"] fn _msrv_1_37() { - #![clippy::msrv = "1.37"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; @@ -55,8 +54,8 @@ fn _msrv_1_37() { let _ = mut_ptr as *mut i32; } +#[clippy::msrv = "1.38"] fn _msrv_1_38() { - #![clippy::msrv = "1.38"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs index ca2616b0069a..302c66462d9b 100644 --- a/tests/ui/ptr_as_ptr.rs +++ b/tests/ui/ptr_as_ptr.rs @@ -2,7 +2,6 @@ // aux-build:macro_rules.rs #![warn(clippy::ptr_as_ptr)] -#![feature(custom_inner_attributes)] extern crate macro_rules; @@ -45,8 +44,8 @@ fn main() { let _ = macro_rules::ptr_as_ptr_cast!(ptr); } +#[clippy::msrv = "1.37"] fn _msrv_1_37() { - #![clippy::msrv = "1.37"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; @@ -55,8 +54,8 @@ fn _msrv_1_37() { let _ = mut_ptr as *mut i32; } +#[clippy::msrv = "1.38"] fn _msrv_1_38() { - #![clippy::msrv = "1.38"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr index c58c55cfd83a..a68e1cab6d35 100644 --- a/tests/ui/ptr_as_ptr.stderr +++ b/tests/ui/ptr_as_ptr.stderr @@ -1,5 +1,5 @@ error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:19:13 + --> $DIR/ptr_as_ptr.rs:18:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` @@ -7,31 +7,31 @@ LL | let _ = ptr as *const i32; = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:20:13 + --> $DIR/ptr_as_ptr.rs:19:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:25:17 + --> $DIR/ptr_as_ptr.rs:24:17 | LL | let _ = *ptr_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:38:25 + --> $DIR/ptr_as_ptr.rs:37:25 | LL | let _: *const i32 = ptr as *const _; | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:39:23 + --> $DIR/ptr_as_ptr.rs:38:23 | LL | let _: *mut i32 = mut_ptr as _; | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:11:9 + --> $DIR/ptr_as_ptr.rs:10:9 | LL | $ptr as *const i32 | ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::()` @@ -42,13 +42,13 @@ LL | let _ = cast_it!(ptr); = note: this error originates in the macro `cast_it` (in Nightly builds, run with -Z macro-backtrace for more info) error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:63:13 + --> $DIR/ptr_as_ptr.rs:62:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:64:13 + --> $DIR/ptr_as_ptr.rs:63:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 824f00cb99e8..4923731fe45e 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_range_contains)] #![allow(unused)] #![allow(clippy::no_effect)] @@ -65,16 +64,14 @@ pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let x = 5; x >= 8 && x < 34; } +#[clippy::msrv = "1.35"] fn msrv_1_35() { - #![clippy::msrv = "1.35"] - let x = 5; (8..35).contains(&x); } diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index df925eeadfe5..d623ccb5da63 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_range_contains)] #![allow(unused)] #![allow(clippy::no_effect)] @@ -65,16 +64,14 @@ pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } +#[clippy::msrv = "1.34"] fn msrv_1_34() { - #![clippy::msrv = "1.34"] - let x = 5; x >= 8 && x < 34; } +#[clippy::msrv = "1.35"] fn msrv_1_35() { - #![clippy::msrv = "1.35"] - let x = 5; x >= 8 && x < 35; } diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 9689e665b05c..ea34023a4664 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -1,5 +1,5 @@ error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:14:5 + --> $DIR/range_contains.rs:13:5 | LL | x >= 8 && x < 12; | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` @@ -7,121 +7,121 @@ LL | x >= 8 && x < 12; = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:15:5 + --> $DIR/range_contains.rs:14:5 | LL | x < 42 && x >= 21; | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:16:5 + --> $DIR/range_contains.rs:15:5 | LL | 100 > x && 1 <= x; | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:19:5 + --> $DIR/range_contains.rs:18:5 | LL | x >= 9 && x <= 99; | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:20:5 + --> $DIR/range_contains.rs:19:5 | LL | x <= 33 && x >= 1; | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:21:5 + --> $DIR/range_contains.rs:20:5 | LL | 999 >= x && 1 <= x; | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:24:5 + --> $DIR/range_contains.rs:23:5 | LL | x < 8 || x >= 12; | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:25:5 + --> $DIR/range_contains.rs:24:5 | LL | x >= 42 || x < 21; | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:26:5 + --> $DIR/range_contains.rs:25:5 | LL | 100 <= x || 1 > x; | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:29:5 + --> $DIR/range_contains.rs:28:5 | LL | x < 9 || x > 99; | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:30:5 + --> $DIR/range_contains.rs:29:5 | LL | x > 33 || x < 1; | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:31:5 + --> $DIR/range_contains.rs:30:5 | LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:46:5 + --> $DIR/range_contains.rs:45:5 | LL | y >= 0. && y < 1.; | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:47:5 + --> $DIR/range_contains.rs:46:5 | LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:50:5 + --> $DIR/range_contains.rs:49:5 | LL | x >= -10 && x <= 10; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:52:5 + --> $DIR/range_contains.rs:51:5 | LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:57:30 + --> $DIR/range_contains.rs:56:30 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:57:5 + --> $DIR/range_contains.rs:56:5 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:58:29 + --> $DIR/range_contains.rs:57:29 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:58:5 + --> $DIR/range_contains.rs:57:5 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:79:5 + --> $DIR/range_contains.rs:76:5 | LL | x >= 8 && x < 35; | ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)` diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index 7cd687c95a00..c0e49ff4caa7 100644 --- a/tests/ui/redundant_closure_call_fixable.fixed +++ b/tests/ui/redundant_closure_call_fixable.fixed @@ -25,4 +25,16 @@ fn main() { x * y }; let d = async { something().await }; + + macro_rules! m { + () => { + 0 + }; + } + macro_rules! m2 { + () => { + m!() + }; + } + m2!(); } diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs index 37e4d2238641..9e6e54348a8c 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs @@ -25,4 +25,16 @@ fn main() { x * y })(); let d = (async || something().await)(); + + macro_rules! m { + () => { + (|| 0)() + }; + } + macro_rules! m2 { + () => { + (|| m!())() + }; + } + m2!(); } diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index 56a8e57c0c36..d71bcba2a820 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -52,5 +52,27 @@ error: try not to call a closure in the expression where it is declared LL | let d = (async || something().await)(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }` -error: aborting due to 4 previous errors +error: try not to call a closure in the expression where it is declared + --> $DIR/redundant_closure_call_fixable.rs:36:13 + | +LL | (|| m!())() + | ^^^^^^^^^^^ help: try doing something like: `m!()` +... +LL | m2!(); + | ----- in this macro invocation + | + = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: try not to call a closure in the expression where it is declared + --> $DIR/redundant_closure_call_fixable.rs:31:13 + | +LL | (|| 0)() + | ^^^^^^^^ help: try doing something like: `0` +... +LL | m2!(); + | ----- in this macro invocation + | + = note: this error originates in the macro `m` which comes from the expansion of the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index 34ab552cb1d8..ec7f8ae923a7 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -72,16 +71,14 @@ fn issue_3476() { S { foo: foo:: }; } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - let start = 0; let _ = RangeFrom { start: start }; } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - let start = 0; let _ = RangeFrom { start }; } diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index a051b1f96f0f..73122016cf69 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::redundant_field_names)] #![allow(clippy::no_effect, dead_code, unused_variables)] @@ -72,16 +71,14 @@ fn issue_3476() { S { foo: foo:: }; } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - let start = 0; let _ = RangeFrom { start: start }; } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - let start = 0; let _ = RangeFrom { start: start }; } diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 8b82e062b93a..00a72c50cf7d 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -1,5 +1,5 @@ error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:36:9 + --> $DIR/redundant_field_names.rs:35:9 | LL | gender: gender, | ^^^^^^^^^^^^^^ help: replace it with: `gender` @@ -7,43 +7,43 @@ LL | gender: gender, = note: `-D clippy::redundant-field-names` implied by `-D warnings` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:37:9 + --> $DIR/redundant_field_names.rs:36:9 | LL | age: age, | ^^^^^^^^ help: replace it with: `age` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:25 + --> $DIR/redundant_field_names.rs:57:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:59:23 + --> $DIR/redundant_field_names.rs:58:23 | LL | let _ = RangeTo { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:60:21 + --> $DIR/redundant_field_names.rs:59:21 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:60:35 + --> $DIR/redundant_field_names.rs:59:35 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:62:32 + --> $DIR/redundant_field_names.rs:61:32 | LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:86:25 + --> $DIR/redundant_field_names.rs:83:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index 42110dbe81e8..4c5846fe837e 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -56,14 +55,12 @@ impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - static V: &'static u8 = &16; } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - static V: &u8 = &17; } diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index bc5200bc8625..64a66be1a83c 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![allow(unused)] #[derive(Debug)] @@ -56,14 +55,12 @@ impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } +#[clippy::msrv = "1.16"] fn msrv_1_16() { - #![clippy::msrv = "1.16"] - static V: &'static u8 = &16; } +#[clippy::msrv = "1.17"] fn msrv_1_17() { - #![clippy::msrv = "1.17"] - static V: &'static u8 = &17; } diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 735113460d28..0938ebf783ff 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -1,5 +1,5 @@ error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:9:17 + --> $DIR/redundant_static_lifetimes.rs:8:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` @@ -7,97 +7,97 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:13:21 + --> $DIR/redundant_static_lifetimes.rs:12:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:15:32 + --> $DIR/redundant_static_lifetimes.rs:14:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:15:47 + --> $DIR/redundant_static_lifetimes.rs:14:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:17:17 + --> $DIR/redundant_static_lifetimes.rs:16:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:19:20 + --> $DIR/redundant_static_lifetimes.rs:18:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:21:19 + --> $DIR/redundant_static_lifetimes.rs:20:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:23:19 + --> $DIR/redundant_static_lifetimes.rs:22:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: constants have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:25:19 + --> $DIR/redundant_static_lifetimes.rs:24:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:27:25 + --> $DIR/redundant_static_lifetimes.rs:26:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:31:29 + --> $DIR/redundant_static_lifetimes.rs:30:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:33:25 + --> $DIR/redundant_static_lifetimes.rs:32:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:35:28 + --> $DIR/redundant_static_lifetimes.rs:34:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:37:27 + --> $DIR/redundant_static_lifetimes.rs:36:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:39:27 + --> $DIR/redundant_static_lifetimes.rs:38:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:41:27 + --> $DIR/redundant_static_lifetimes.rs:40:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:68:16 + --> $DIR/redundant_static_lifetimes.rs:65:16 | LL | static V: &'static u8 = &17; | -^^^^^^^--- help: consider removing `'static`: `&u8` diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index 9dd27d6dc01a..1c12cebfd971 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -108,4 +108,10 @@ pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { Ok(()) } +// Issue #10005 +enum Empty {} +fn _empty_error() -> Result<(), Empty> { + Ok(()) +} + fn main() {} diff --git a/tests/ui/seek_from_current.fixed b/tests/ui/seek_from_current.fixed index 4b5303324bc6..1309c91b81c9 100644 --- a/tests/ui/seek_from_current.fixed +++ b/tests/ui/seek_from_current.fixed @@ -1,12 +1,11 @@ // run-rustfix #![warn(clippy::seek_from_current)] -#![feature(custom_inner_attributes)] use std::fs::File; use std::io::{self, Seek, SeekFrom, Write}; +#[clippy::msrv = "1.50"] fn _msrv_1_50() -> io::Result<()> { - #![clippy::msrv = "1.50"] let mut f = File::create("foo.txt")?; f.write_all(b"Hi!")?; f.seek(SeekFrom::Current(0))?; @@ -14,8 +13,8 @@ fn _msrv_1_50() -> io::Result<()> { Ok(()) } +#[clippy::msrv = "1.51"] fn _msrv_1_51() -> io::Result<()> { - #![clippy::msrv = "1.51"] let mut f = File::create("foo.txt")?; f.write_all(b"Hi!")?; f.stream_position()?; diff --git a/tests/ui/seek_from_current.rs b/tests/ui/seek_from_current.rs index f93639261a18..5d9b1424cf68 100644 --- a/tests/ui/seek_from_current.rs +++ b/tests/ui/seek_from_current.rs @@ -1,12 +1,11 @@ // run-rustfix #![warn(clippy::seek_from_current)] -#![feature(custom_inner_attributes)] use std::fs::File; use std::io::{self, Seek, SeekFrom, Write}; +#[clippy::msrv = "1.50"] fn _msrv_1_50() -> io::Result<()> { - #![clippy::msrv = "1.50"] let mut f = File::create("foo.txt")?; f.write_all(b"Hi!")?; f.seek(SeekFrom::Current(0))?; @@ -14,8 +13,8 @@ fn _msrv_1_50() -> io::Result<()> { Ok(()) } +#[clippy::msrv = "1.51"] fn _msrv_1_51() -> io::Result<()> { - #![clippy::msrv = "1.51"] let mut f = File::create("foo.txt")?; f.write_all(b"Hi!")?; f.seek(SeekFrom::Current(0))?; diff --git a/tests/ui/seek_from_current.stderr b/tests/ui/seek_from_current.stderr index db1125b53cdf..c079f3611929 100644 --- a/tests/ui/seek_from_current.stderr +++ b/tests/ui/seek_from_current.stderr @@ -1,5 +1,5 @@ error: using `SeekFrom::Current` to start from current position - --> $DIR/seek_from_current.rs:21:5 + --> $DIR/seek_from_current.rs:20:5 | LL | f.seek(SeekFrom::Current(0))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `f.stream_position()` diff --git a/tests/ui/seek_to_start_instead_of_rewind.fixed b/tests/ui/seek_to_start_instead_of_rewind.fixed index 464b6cdef639..9d0d1124c460 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.fixed +++ b/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -1,6 +1,5 @@ // run-rustfix #![allow(unused)] -#![feature(custom_inner_attributes)] #![warn(clippy::seek_to_start_instead_of_rewind)] use std::fs::OpenOptions; @@ -94,9 +93,8 @@ fn main() { assert_eq!(&buf, hello); } +#[clippy::msrv = "1.54"] fn msrv_1_54() { - #![clippy::msrv = "1.54"] - let mut f = OpenOptions::new() .write(true) .read(true) @@ -115,9 +113,8 @@ fn msrv_1_54() { assert_eq!(&buf, hello); } +#[clippy::msrv = "1.55"] fn msrv_1_55() { - #![clippy::msrv = "1.55"] - let mut f = OpenOptions::new() .write(true) .read(true) diff --git a/tests/ui/seek_to_start_instead_of_rewind.rs b/tests/ui/seek_to_start_instead_of_rewind.rs index 68e09bd7c1f0..c5bc57cc3a74 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.rs +++ b/tests/ui/seek_to_start_instead_of_rewind.rs @@ -1,6 +1,5 @@ // run-rustfix #![allow(unused)] -#![feature(custom_inner_attributes)] #![warn(clippy::seek_to_start_instead_of_rewind)] use std::fs::OpenOptions; @@ -94,9 +93,8 @@ fn main() { assert_eq!(&buf, hello); } +#[clippy::msrv = "1.54"] fn msrv_1_54() { - #![clippy::msrv = "1.54"] - let mut f = OpenOptions::new() .write(true) .read(true) @@ -115,9 +113,8 @@ fn msrv_1_54() { assert_eq!(&buf, hello); } +#[clippy::msrv = "1.55"] fn msrv_1_55() { - #![clippy::msrv = "1.55"] - let mut f = OpenOptions::new() .write(true) .read(true) diff --git a/tests/ui/seek_to_start_instead_of_rewind.stderr b/tests/ui/seek_to_start_instead_of_rewind.stderr index de0eec5d909c..6cce025359fe 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.stderr +++ b/tests/ui/seek_to_start_instead_of_rewind.stderr @@ -1,5 +1,5 @@ error: used `seek` to go to the start of the stream - --> $DIR/seek_to_start_instead_of_rewind.rs:54:7 + --> $DIR/seek_to_start_instead_of_rewind.rs:53:7 | LL | t.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` @@ -7,13 +7,13 @@ LL | t.seek(SeekFrom::Start(0)); = note: `-D clippy::seek-to-start-instead-of-rewind` implied by `-D warnings` error: used `seek` to go to the start of the stream - --> $DIR/seek_to_start_instead_of_rewind.rs:59:7 + --> $DIR/seek_to_start_instead_of_rewind.rs:58:7 | LL | t.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` error: used `seek` to go to the start of the stream - --> $DIR/seek_to_start_instead_of_rewind.rs:131:7 + --> $DIR/seek_to_start_instead_of_rewind.rs:128:7 | LL | f.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index e5fe9133f975..074dae5fb286 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::transmute_ptr_to_ref)] #![allow(clippy::match_single_binding)] @@ -51,8 +50,8 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } } +#[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.38"] let a = 0u32; let a = &a as *const u32; let _: &u32 = &*a; @@ -63,8 +62,8 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +#[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.37"] let a = 0u32; let a = &a as *const u32; let _: &u32 = &*a; diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index fe49cdc324fd..2edc122cf471 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::transmute_ptr_to_ref)] #![allow(clippy::match_single_binding)] @@ -51,8 +50,8 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } } +#[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.38"] let a = 0u32; let a = &a as *const u32; let _: &u32 = std::mem::transmute(a); @@ -63,8 +62,8 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +#[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.37"] let a = 0u32; let a = &a as *const u32; let _: &u32 = std::mem::transmute(a); diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 10117ee9182a..b3e6c09d2d7a 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -1,5 +1,5 @@ error: transmute from a pointer type (`*const T`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:8:17 + --> $DIR/transmute_ptr_to_ref.rs:7:17 | LL | let _: &T = std::mem::transmute(p); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` @@ -7,127 +7,127 @@ LL | let _: &T = std::mem::transmute(p); = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:11:21 + --> $DIR/transmute_ptr_to_ref.rs:10:21 | LL | let _: &mut T = std::mem::transmute(m); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` error: transmute from a pointer type (`*mut T`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:14:17 + --> $DIR/transmute_ptr_to_ref.rs:13:17 | LL | let _: &T = std::mem::transmute(m); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:17:21 + --> $DIR/transmute_ptr_to_ref.rs:16:21 | LL | let _: &mut T = std::mem::transmute(p as *mut T); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` error: transmute from a pointer type (`*const U`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:20:17 + --> $DIR/transmute_ptr_to_ref.rs:19:17 | LL | let _: &T = std::mem::transmute(o); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:23:21 + --> $DIR/transmute_ptr_to_ref.rs:22:21 | LL | let _: &mut T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:26:17 + --> $DIR/transmute_ptr_to_ref.rs:25:17 | LL | let _: &T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) - --> $DIR/transmute_ptr_to_ref.rs:36:32 + --> $DIR/transmute_ptr_to_ref.rs:35:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) - --> $DIR/transmute_ptr_to_ref.rs:38:33 + --> $DIR/transmute_ptr_to_ref.rs:37:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) - --> $DIR/transmute_ptr_to_ref.rs:42:14 + --> $DIR/transmute_ptr_to_ref.rs:41:14 | LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:47:14 + --> $DIR/transmute_ptr_to_ref.rs:46:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:48:14 + --> $DIR/transmute_ptr_to_ref.rs:47:14 | LL | 1 => std::mem::transmute(y), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:49:14 + --> $DIR/transmute_ptr_to_ref.rs:48:14 | LL | 2 => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:50:14 + --> $DIR/transmute_ptr_to_ref.rs:49:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(y), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:58:19 + --> $DIR/transmute_ptr_to_ref.rs:57:19 | LL | let _: &u32 = std::mem::transmute(a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:59:19 + --> $DIR/transmute_ptr_to_ref.rs:58:19 | LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:61:14 + --> $DIR/transmute_ptr_to_ref.rs:60:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:62:14 + --> $DIR/transmute_ptr_to_ref.rs:61:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:70:19 + --> $DIR/transmute_ptr_to_ref.rs:69:19 | LL | let _: &u32 = std::mem::transmute(a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:71:19 + --> $DIR/transmute_ptr_to_ref.rs:70:19 | LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:73:14 + --> $DIR/transmute_ptr_to_ref.rs:72:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:74:14 + --> $DIR/transmute_ptr_to_ref.rs:73:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index cbc6768033ec..c05eb447b2eb 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,6 +1,6 @@ // aux-build:proc_macro_unsafe.rs -#![warn(clippy::undocumented_unsafe_blocks)] +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] #![allow(clippy::let_unit_value, clippy::missing_safety_doc)] extern crate proc_macro_unsafe; diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index ba4de9806d17..d1c1bb5ffeac 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -239,6 +239,19 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: | = help: consider adding a safety comment on the preceding line +error: constant item has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:471:5 + | +LL | const BIG_NUMBER: i32 = 1000000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:470:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + error: unsafe impl missing a safety comment --> $DIR/undocumented_unsafe_blocks.rs:472:5 | @@ -271,6 +284,24 @@ LL | unsafe {}; | = help: consider adding a safety comment on the preceding line +error: statement has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:501:5 + | +LL | / let _ = { +LL | | if unsafe { true } { +LL | | todo!(); +LL | | } else { +... | +LL | | } +LL | | }; + | |______^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:500:5 + | +LL | // SAFETY: this is more than one level away, so it should warn + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: unsafe block missing a safety comment --> $DIR/undocumented_unsafe_blocks.rs:502:12 | @@ -287,5 +318,5 @@ LL | let bar = unsafe {}; | = help: consider adding a safety comment on the preceding line -error: aborting due to 34 previous errors +error: aborting due to 36 previous errors diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index 106274479751..9d08e80cf9a5 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -1,6 +1,5 @@ // aux-build:proc_macro_with_span.rs // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::uninlined_format_args)] #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] @@ -44,9 +43,7 @@ fn tester(fn_arg: i32) { println!("val='{local_i32}'"); // space+tab println!("val='{local_i32}'"); // tab+space println!( - "val='{ - }'", - local_i32 + "val='{local_i32}'" ); println!("{local_i32}"); println!("{fn_arg}"); @@ -57,11 +54,11 @@ fn tester(fn_arg: i32) { println!("{local_i32:<3}"); println!("{local_i32:#010x}"); println!("{local_f64:.1}"); - println!("Hello {} is {local_f64:.local_i32$}", "x"); - println!("Hello {local_i32} is {local_f64:.*}", 5); - println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("Hello {} is {:.*}", "x", local_i32, local_f64); + println!("Hello {} is {:.*}", local_i32, 5, local_f64); + println!("Hello {} is {2:.*}", local_i32, 5, local_f64); println!("{local_i32} {local_f64}"); - println!("{local_i32}, {}", local_opt.unwrap()); + println!("{}, {}", local_i32, local_opt.unwrap()); println!("{val}"); println!("{val}"); println!("{} {1}", local_i32, 42); @@ -110,8 +107,7 @@ fn tester(fn_arg: i32) { println!("{local_f64:width$.prec$}"); println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); println!( - "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", - local_i32, width, prec, + "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", ); println!( "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", @@ -142,9 +138,7 @@ fn tester(fn_arg: i32) { println!(no_param_str!(), local_i32); println!( - "{}", - // comment with a comma , in it - val, + "{val}", ); println!("{val}"); @@ -169,14 +163,14 @@ fn main() { tester(42); } +#[clippy::msrv = "1.57"] fn _under_msrv() { - #![clippy::msrv = "1.57"] let local_i32 = 1; println!("don't expand='{}'", local_i32); } +#[clippy::msrv = "1.58"] fn _meets_msrv() { - #![clippy::msrv = "1.58"] let local_i32 = 1; println!("expand='{local_i32}'"); } diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs index 8e495ebd083a..35b3677a8968 100644 --- a/tests/ui/uninlined_format_args.rs +++ b/tests/ui/uninlined_format_args.rs @@ -1,6 +1,5 @@ // aux-build:proc_macro_with_span.rs // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::uninlined_format_args)] #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] @@ -169,14 +168,14 @@ fn main() { tester(42); } +#[clippy::msrv = "1.57"] fn _under_msrv() { - #![clippy::msrv = "1.57"] let local_i32 = 1; println!("don't expand='{}'", local_i32); } +#[clippy::msrv = "1.58"] fn _meets_msrv() { - #![clippy::msrv = "1.58"] let local_i32 = 1; println!("expand='{}'", local_i32); } diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index 2ce3b7fa960c..a12abf8bef8a 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -1,5 +1,5 @@ error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:41:5 + --> $DIR/uninlined_format_args.rs:40:5 | LL | println!("val='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:42:5 + --> $DIR/uninlined_format_args.rs:41:5 | LL | println!("val='{ }'", local_i32); // 3 spaces | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL + println!("val='{local_i32}'"); // 3 spaces | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:43:5 + --> $DIR/uninlined_format_args.rs:42:5 | LL | println!("val='{ }'", local_i32); // tab | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL + println!("val='{local_i32}'"); // tab | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:44:5 + --> $DIR/uninlined_format_args.rs:43:5 | LL | println!("val='{ }'", local_i32); // space+tab | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + println!("val='{local_i32}'"); // space+tab | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:45:5 + --> $DIR/uninlined_format_args.rs:44:5 | LL | println!("val='{ }'", local_i32); // tab+space | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,17 @@ LL + println!("val='{local_i32}'"); // tab+space | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:51:5 + --> $DIR/uninlined_format_args.rs:45:5 + | +LL | / println!( +LL | | "val='{ +LL | | }'", +LL | | local_i32 +LL | | ); + | |_____^ + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:50:5 | LL | println!("{}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +82,7 @@ LL + println!("{local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:52:5 + --> $DIR/uninlined_format_args.rs:51:5 | LL | println!("{}", fn_arg); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +94,7 @@ LL + println!("{fn_arg}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:53:5 + --> $DIR/uninlined_format_args.rs:52:5 | LL | println!("{:?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +106,7 @@ LL + println!("{local_i32:?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:54:5 + --> $DIR/uninlined_format_args.rs:53:5 | LL | println!("{:#?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -108,7 +118,7 @@ LL + println!("{local_i32:#?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:55:5 + --> $DIR/uninlined_format_args.rs:54:5 | LL | println!("{:4}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +130,7 @@ LL + println!("{local_i32:4}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:56:5 + --> $DIR/uninlined_format_args.rs:55:5 | LL | println!("{:04}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +142,7 @@ LL + println!("{local_i32:04}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:57:5 + --> $DIR/uninlined_format_args.rs:56:5 | LL | println!("{:<3}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +154,7 @@ LL + println!("{local_i32:<3}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:58:5 + --> $DIR/uninlined_format_args.rs:57:5 | LL | println!("{:#010x}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -156,7 +166,7 @@ LL + println!("{local_i32:#010x}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:59:5 + --> $DIR/uninlined_format_args.rs:58:5 | LL | println!("{:.1}", local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -167,45 +177,9 @@ LL - println!("{:.1}", local_f64); LL + println!("{local_f64:.1}"); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:60:5 - | -LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); -LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:61:5 - | -LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64); -LL + println!("Hello {local_i32} is {local_f64:.*}", 5); - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:62:5 | -LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64); -LL + println!("Hello {local_i32} is {local_f64:.*}", 5); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:63:5 - | LL | println!("{} {}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -218,18 +192,6 @@ LL + println!("{local_i32} {local_f64}"); error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:64:5 | -LL | println!("{}, {}", local_i32, local_opt.unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{}, {}", local_i32, local_opt.unwrap()); -LL + println!("{local_i32}, {}", local_opt.unwrap()); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:65:5 - | LL | println!("{}", val); | ^^^^^^^^^^^^^^^^^^^ | @@ -240,7 +202,7 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:66:5 + --> $DIR/uninlined_format_args.rs:65:5 | LL | println!("{}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -252,7 +214,7 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:68:5 + --> $DIR/uninlined_format_args.rs:67:5 | LL | println!("val='{/t }'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -264,7 +226,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:69:5 + --> $DIR/uninlined_format_args.rs:68:5 | LL | println!("val='{/n }'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -276,7 +238,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:70:5 + --> $DIR/uninlined_format_args.rs:69:5 | LL | println!("val='{local_i32}'", local_i32 = local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -288,7 +250,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:71:5 + --> $DIR/uninlined_format_args.rs:70:5 | LL | println!("val='{local_i32}'", local_i32 = fn_arg); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -300,7 +262,7 @@ LL + println!("val='{fn_arg}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:72:5 + --> $DIR/uninlined_format_args.rs:71:5 | LL | println!("{0}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -312,7 +274,7 @@ LL + println!("{local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:73:5 + --> $DIR/uninlined_format_args.rs:72:5 | LL | println!("{0:?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -324,7 +286,7 @@ LL + println!("{local_i32:?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:74:5 + --> $DIR/uninlined_format_args.rs:73:5 | LL | println!("{0:#?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -336,7 +298,7 @@ LL + println!("{local_i32:#?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:75:5 + --> $DIR/uninlined_format_args.rs:74:5 | LL | println!("{0:04}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -348,7 +310,7 @@ LL + println!("{local_i32:04}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:76:5 + --> $DIR/uninlined_format_args.rs:75:5 | LL | println!("{0:<3}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -360,7 +322,7 @@ LL + println!("{local_i32:<3}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:77:5 + --> $DIR/uninlined_format_args.rs:76:5 | LL | println!("{0:#010x}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -372,7 +334,7 @@ LL + println!("{local_i32:#010x}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:78:5 + --> $DIR/uninlined_format_args.rs:77:5 | LL | println!("{0:.1}", local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -384,7 +346,7 @@ LL + println!("{local_f64:.1}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:79:5 + --> $DIR/uninlined_format_args.rs:78:5 | LL | println!("{0} {0}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -396,7 +358,7 @@ LL + println!("{local_i32} {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:80:5 + --> $DIR/uninlined_format_args.rs:79:5 | LL | println!("{1} {} {0} {}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -408,7 +370,7 @@ LL + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:81:5 + --> $DIR/uninlined_format_args.rs:80:5 | LL | println!("{0} {1}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -420,7 +382,7 @@ LL + println!("{local_i32} {local_f64}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:82:5 + --> $DIR/uninlined_format_args.rs:81:5 | LL | println!("{1} {0}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -432,7 +394,7 @@ LL + println!("{local_f64} {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:83:5 + --> $DIR/uninlined_format_args.rs:82:5 | LL | println!("{1} {0} {1} {0}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -444,7 +406,7 @@ LL + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:85:5 + --> $DIR/uninlined_format_args.rs:84:5 | LL | println!("{v}", v = local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -456,7 +418,7 @@ LL + println!("{local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:86:5 + --> $DIR/uninlined_format_args.rs:85:5 | LL | println!("{local_i32:0$}", width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -468,7 +430,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:87:5 + --> $DIR/uninlined_format_args.rs:86:5 | LL | println!("{local_i32:w$}", w = width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -480,7 +442,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:88:5 + --> $DIR/uninlined_format_args.rs:87:5 | LL | println!("{local_i32:.0$}", prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -492,7 +454,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:89:5 + --> $DIR/uninlined_format_args.rs:88:5 | LL | println!("{local_i32:.p$}", p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -504,7 +466,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:90:5 + --> $DIR/uninlined_format_args.rs:89:5 | LL | println!("{:0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -516,7 +478,7 @@ LL + println!("{val:val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:91:5 + --> $DIR/uninlined_format_args.rs:90:5 | LL | println!("{0:0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -528,7 +490,7 @@ LL + println!("{val:val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:92:5 + --> $DIR/uninlined_format_args.rs:91:5 | LL | println!("{:0$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -540,7 +502,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:93:5 + --> $DIR/uninlined_format_args.rs:92:5 | LL | println!("{0:0$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -552,7 +514,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:94:5 + --> $DIR/uninlined_format_args.rs:93:5 | LL | println!("{0:0$.v$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -564,7 +526,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:95:5 + --> $DIR/uninlined_format_args.rs:94:5 | LL | println!("{0:v$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -576,7 +538,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:96:5 + --> $DIR/uninlined_format_args.rs:95:5 | LL | println!("{v:0$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -588,7 +550,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:97:5 + --> $DIR/uninlined_format_args.rs:96:5 | LL | println!("{v:v$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -600,7 +562,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:98:5 + --> $DIR/uninlined_format_args.rs:97:5 | LL | println!("{v:0$.v$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -612,7 +574,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:99:5 + --> $DIR/uninlined_format_args.rs:98:5 | LL | println!("{v:v$.v$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -624,7 +586,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:100:5 + --> $DIR/uninlined_format_args.rs:99:5 | LL | println!("{:0$}", width); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -636,7 +598,7 @@ LL + println!("{width:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:101:5 + --> $DIR/uninlined_format_args.rs:100:5 | LL | println!("{:1$}", local_i32, width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -648,7 +610,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:102:5 + --> $DIR/uninlined_format_args.rs:101:5 | LL | println!("{:w$}", w = width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -660,7 +622,7 @@ LL + println!("{width:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:103:5 + --> $DIR/uninlined_format_args.rs:102:5 | LL | println!("{:w$}", local_i32, w = width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -672,7 +634,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:104:5 + --> $DIR/uninlined_format_args.rs:103:5 | LL | println!("{:.0$}", prec); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -684,7 +646,7 @@ LL + println!("{prec:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:105:5 + --> $DIR/uninlined_format_args.rs:104:5 | LL | println!("{:.1$}", local_i32, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -696,7 +658,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:106:5 + --> $DIR/uninlined_format_args.rs:105:5 | LL | println!("{:.p$}", p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -708,7 +670,7 @@ LL + println!("{prec:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:107:5 + --> $DIR/uninlined_format_args.rs:106:5 | LL | println!("{:.p$}", local_i32, p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -720,7 +682,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:108:5 + --> $DIR/uninlined_format_args.rs:107:5 | LL | println!("{:0$.1$}", width, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -732,7 +694,7 @@ LL + println!("{width:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:109:5 + --> $DIR/uninlined_format_args.rs:108:5 | LL | println!("{:0$.w$}", width, w = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -744,7 +706,7 @@ LL + println!("{width:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:110:5 + --> $DIR/uninlined_format_args.rs:109:5 | LL | println!("{:1$.2$}", local_f64, width, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -756,7 +718,7 @@ LL + println!("{local_f64:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:111:5 + --> $DIR/uninlined_format_args.rs:110:5 | LL | println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -768,7 +730,16 @@ LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:123:5 + --> $DIR/uninlined_format_args.rs:111:5 + | +LL | / println!( +LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", +LL | | local_i32, width, prec, +LL | | ); + | |_____^ + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:122:5 | LL | println!("Width = {}, value with width = {:0$}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -780,7 +751,7 @@ LL + println!("Width = {local_i32}, value with width = {local_f64:local_i32$ | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:124:5 + --> $DIR/uninlined_format_args.rs:123:5 | LL | println!("{:w$.p$}", local_i32, w = width, p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -792,7 +763,7 @@ LL + println!("{local_i32:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:125:5 + --> $DIR/uninlined_format_args.rs:124:5 | LL | println!("{:w$.p$}", w = width, p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -804,7 +775,7 @@ LL + println!("{width:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:126:20 + --> $DIR/uninlined_format_args.rs:125:20 | LL | println!("{}", format!("{}", local_i32)); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -816,7 +787,17 @@ LL + println!("{}", format!("{local_i32}")); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:149:5 + --> $DIR/uninlined_format_args.rs:143:5 + | +LL | / println!( +LL | | "{}", +LL | | // comment with a comma , in it +LL | | val, +LL | | ); + | |_____^ + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:148:5 | LL | println!("{}", /* comment with a comma , in it */ val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -828,7 +809,7 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:155:9 + --> $DIR/uninlined_format_args.rs:154:9 | LL | panic!("p1 {}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -840,7 +821,7 @@ LL + panic!("p1 {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:158:9 + --> $DIR/uninlined_format_args.rs:157:9 | LL | panic!("p2 {0}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -852,7 +833,7 @@ LL + panic!("p2 {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:161:9 + --> $DIR/uninlined_format_args.rs:160:9 | LL | panic!("p3 {local_i32}", local_i32 = local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -864,7 +845,7 @@ LL + panic!("p3 {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:181:5 + --> $DIR/uninlined_format_args.rs:180:5 | LL | println!("expand='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -875,5 +856,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 73 previous errors +error: aborting due to 72 previous errors diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index ec8c6abfab91..2f7e2997e739 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -41,6 +41,17 @@ fn main() { // do not lint cast to alias type 1 as I32Alias; &1 as &I32Alias; + + // issue #9960 + macro_rules! bind_var { + ($id:ident, $e:expr) => {{ + let $id = 0usize; + let _ = $e != 0usize; + let $id = 0isize; + let _ = $e != 0usize; + }} + } + bind_var!(x, (x as usize) + 1); } type I32Alias = i32; @@ -85,6 +96,9 @@ mod fixable { let _ = 1 as I32Alias; let _ = &1 as &I32Alias; + + let x = 1i32; + let _ = &{ x }; } type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 5213cdc269bd..54dd46ba59f1 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -41,6 +41,17 @@ fn main() { // do not lint cast to alias type 1 as I32Alias; &1 as &I32Alias; + + // issue #9960 + macro_rules! bind_var { + ($id:ident, $e:expr) => {{ + let $id = 0usize; + let _ = $e != 0usize; + let $id = 0isize; + let _ = $e != 0usize; + }} + } + bind_var!(x, (x as usize) + 1); } type I32Alias = i32; @@ -85,6 +96,9 @@ mod fixable { let _ = 1 as I32Alias; let _ = &1 as &I32Alias; + + let x = 1i32; + let _ = &(x as i32); } type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index e5c3dd5e53f8..fcee4ee2a65c 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -49,136 +49,142 @@ LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:53:9 + --> $DIR/unnecessary_cast.rs:64:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:54:9 + --> $DIR/unnecessary_cast.rs:65:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:55:9 + --> $DIR/unnecessary_cast.rs:66:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:56:17 + --> $DIR/unnecessary_cast.rs:67:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:57:17 + --> $DIR/unnecessary_cast.rs:68:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:58:17 + --> $DIR/unnecessary_cast.rs:69:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:59:9 + --> $DIR/unnecessary_cast.rs:70:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:60:9 + --> $DIR/unnecessary_cast.rs:71:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast.rs:72:9 + --> $DIR/unnecessary_cast.rs:83:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:73:9 + --> $DIR/unnecessary_cast.rs:84:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast.rs:74:9 + --> $DIR/unnecessary_cast.rs:85:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast.rs:75:9 + --> $DIR/unnecessary_cast.rs:86:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast.rs:76:9 + --> $DIR/unnecessary_cast.rs:87:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:78:9 + --> $DIR/unnecessary_cast.rs:89:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:79:9 + --> $DIR/unnecessary_cast.rs:90:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:83:17 + --> $DIR/unnecessary_cast.rs:94:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:84:17 + --> $DIR/unnecessary_cast.rs:95:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` +error: casting to the same type is unnecessary (`i32` -> `i32`) + --> $DIR/unnecessary_cast.rs:101:18 + | +LL | let _ = &(x as i32); + | ^^^^^^^^^^ help: try: `{ x }` + error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:93:22 + --> $DIR/unnecessary_cast.rs:107:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> $DIR/unnecessary_cast.rs:95:22 + --> $DIR/unnecessary_cast.rs:109:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:102:22 + --> $DIR/unnecessary_cast.rs:116:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:104:23 + --> $DIR/unnecessary_cast.rs:118:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> $DIR/unnecessary_cast.rs:112:20 + --> $DIR/unnecessary_cast.rs:126:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` -error: aborting due to 30 previous errors +error: aborting due to 31 previous errors diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index ce4a82e02174..22e9bd8bdc51 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -33,6 +33,14 @@ impl Drop for Issue9427 { } } +struct Issue9427FollowUp; + +impl Drop for Issue9427FollowUp { + fn drop(&mut self) { + panic!("side effect drop"); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -87,6 +95,7 @@ fn main() { // Should not lint - bool let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); @@ -133,13 +142,13 @@ fn main() { let _: Result = res.or(Ok(astronomers_pi)); let _: Result = res.or(Ok(ext_str.some_field)); let _: Result = res. - // some lines - // some lines - // some lines - // some lines - // some lines - // some lines - or(Ok(ext_str.some_field)); + // some lines + // some lines + // some lines + // some lines + // some lines + // some lines + or(Ok(ext_str.some_field)); // neither bind_instead_of_map nor unnecessary_lazy_eval applies here let _: Result = res.and_then(|x| Err(x)); diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 59cdf6628546..8726d84a23fc 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -33,6 +33,14 @@ impl Drop for Issue9427 { } } +struct Issue9427FollowUp; + +impl Drop for Issue9427FollowUp { + fn drop(&mut self) { + panic!("side effect drop"); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -87,6 +95,7 @@ fn main() { // Should not lint - bool let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); @@ -133,13 +142,13 @@ fn main() { let _: Result = res.or_else(|_| Ok(astronomers_pi)); let _: Result = res.or_else(|_| Ok(ext_str.some_field)); let _: Result = res. - // some lines - // some lines - // some lines - // some lines - // some lines - // some lines - or_else(|_| Ok(ext_str.some_field)); + // some lines + // some lines + // some lines + // some lines + // some lines + // some lines + or_else(|_| Ok(ext_str.some_field)); // neither bind_instead_of_map nor unnecessary_lazy_eval applies here let _: Result = res.and_then(|x| Err(x)); diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 8a9ece4aa7e5..0339755442c5 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^-------------------- @@ -9,7 +9,7 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^--------------------------------- @@ -17,7 +17,7 @@ LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^------------------------------------- @@ -25,7 +25,7 @@ LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:60:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^--------------------- @@ -33,7 +33,7 @@ LL | let _ = opt.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:53:13 + --> $DIR/unnecessary_lazy_eval.rs:61:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^------------------- @@ -41,7 +41,7 @@ LL | let _ = opt.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:13 + --> $DIR/unnecessary_lazy_eval.rs:62:13 | LL | let _ = opt.or_else(|| None); | ^^^^---------------- @@ -49,7 +49,7 @@ LL | let _ = opt.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:55:13 + --> $DIR/unnecessary_lazy_eval.rs:63:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^------------------------ @@ -57,7 +57,7 @@ LL | let _ = opt.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:56:13 + --> $DIR/unnecessary_lazy_eval.rs:64:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^---------------- @@ -65,7 +65,7 @@ LL | let _ = opt.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:57:13 + --> $DIR/unnecessary_lazy_eval.rs:65:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | ^^^^^^^^^^^^^^^^^------------------------------- @@ -73,7 +73,7 @@ LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:58:13 + --> $DIR/unnecessary_lazy_eval.rs:66:13 | LL | let _ = cond.then(|| astronomers_pi); | ^^^^^----------------------- @@ -81,7 +81,7 @@ LL | let _ = cond.then(|| astronomers_pi); | help: use `then_some(..)` instead: `then_some(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:61:13 + --> $DIR/unnecessary_lazy_eval.rs:69:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^-------------------- @@ -89,7 +89,7 @@ LL | let _ = Some(10).unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:62:13 + --> $DIR/unnecessary_lazy_eval.rs:70:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^--------------------- @@ -97,7 +97,7 @@ LL | let _ = Some(10).and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:63:28 + --> $DIR/unnecessary_lazy_eval.rs:71:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^------------------- @@ -105,7 +105,7 @@ LL | let _: Option = None.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:64:13 + --> $DIR/unnecessary_lazy_eval.rs:72:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^------------------------ @@ -113,7 +113,7 @@ LL | let _ = None.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:65:35 + --> $DIR/unnecessary_lazy_eval.rs:73:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^---------------- @@ -121,7 +121,7 @@ LL | let _: Result = None.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:66:28 + --> $DIR/unnecessary_lazy_eval.rs:74:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^---------------- @@ -129,7 +129,7 @@ LL | let _: Option = None.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:69:13 + --> $DIR/unnecessary_lazy_eval.rs:77:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^-------------------- @@ -137,7 +137,7 @@ LL | let _ = deep.0.unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:70:13 + --> $DIR/unnecessary_lazy_eval.rs:78:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^--------------------- @@ -145,7 +145,7 @@ LL | let _ = deep.0.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:71:13 + --> $DIR/unnecessary_lazy_eval.rs:79:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^---------------- @@ -153,7 +153,7 @@ LL | let _ = deep.0.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:72:13 + --> $DIR/unnecessary_lazy_eval.rs:80:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^------------------------ @@ -161,7 +161,7 @@ LL | let _ = deep.0.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:73:13 + --> $DIR/unnecessary_lazy_eval.rs:81:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^---------------- @@ -169,7 +169,7 @@ LL | let _ = deep.0.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:96:28 + --> $DIR/unnecessary_lazy_eval.rs:105:28 | LL | let _: Option = None.or_else(|| Some(3)); | ^^^^^------------------- @@ -177,7 +177,7 @@ LL | let _: Option = None.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:97:13 + --> $DIR/unnecessary_lazy_eval.rs:106:13 | LL | let _ = deep.0.or_else(|| Some(3)); | ^^^^^^^------------------- @@ -185,7 +185,7 @@ LL | let _ = deep.0.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:98:13 + --> $DIR/unnecessary_lazy_eval.rs:107:13 | LL | let _ = opt.or_else(|| Some(3)); | ^^^^------------------- @@ -193,7 +193,7 @@ LL | let _ = opt.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:104:13 + --> $DIR/unnecessary_lazy_eval.rs:113:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^--------------------- @@ -201,7 +201,7 @@ LL | let _ = res2.unwrap_or_else(|_| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:105:13 + --> $DIR/unnecessary_lazy_eval.rs:114:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^---------------------------------- @@ -209,7 +209,7 @@ LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:106:13 + --> $DIR/unnecessary_lazy_eval.rs:115:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^-------------------------------------- @@ -217,7 +217,7 @@ LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:128:35 + --> $DIR/unnecessary_lazy_eval.rs:137:35 | LL | let _: Result = res.and_then(|_| Err(2)); | ^^^^-------------------- @@ -225,7 +225,7 @@ LL | let _: Result = res.and_then(|_| Err(2)); | help: use `and(..)` instead: `and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:129:35 + --> $DIR/unnecessary_lazy_eval.rs:138:35 | LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | ^^^^--------------------------------- @@ -233,7 +233,7 @@ LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | help: use `and(..)` instead: `and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:130:35 + --> $DIR/unnecessary_lazy_eval.rs:139:35 | LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); | ^^^^------------------------------------- @@ -241,7 +241,7 @@ LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)) | help: use `and(..)` instead: `and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:132:35 + --> $DIR/unnecessary_lazy_eval.rs:141:35 | LL | let _: Result = res.or_else(|_| Ok(2)); | ^^^^------------------ @@ -249,7 +249,7 @@ LL | let _: Result = res.or_else(|_| Ok(2)); | help: use `or(..)` instead: `or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:133:35 + --> $DIR/unnecessary_lazy_eval.rs:142:35 | LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | ^^^^------------------------------- @@ -257,7 +257,7 @@ LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | help: use `or(..)` instead: `or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:134:35 + --> $DIR/unnecessary_lazy_eval.rs:143:35 | LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | ^^^^----------------------------------- @@ -265,19 +265,19 @@ LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:135:35 + --> $DIR/unnecessary_lazy_eval.rs:144:35 | LL | let _: Result = res. | ___________________________________^ -LL | | // some lines -LL | | // some lines -LL | | // some lines +LL | | // some lines +LL | | // some lines +LL | | // some lines ... | -LL | | // some lines -LL | | or_else(|_| Ok(ext_str.some_field)); - | |_________----------------------------------^ - | | - | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` +LL | | // some lines +LL | | or_else(|_| Ok(ext_str.some_field)); + | |_____----------------------------------^ + | | + | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: aborting due to 34 previous errors diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index bf0ec8deb345..d37163570abe 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -76,4 +76,13 @@ fn main() { DropStruct { ..get_drop_struct() }; DropEnum::Tuple(get_number()); DropEnum::Struct { field: get_number() }; + + // Issue #9954 + fn one() -> i8 { + 1 + } + macro_rules! use_expr { + ($($e:expr),*) => {{ $($e;)* }} + } + use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one()); } diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index 08cb9ab522ee..a14fd4bca0ef 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -80,4 +80,13 @@ fn main() { DropStruct { ..get_drop_struct() }; DropEnum::Tuple(get_number()); DropEnum::Struct { field: get_number() }; + + // Issue #9954 + fn one() -> i8 { + 1 + } + macro_rules! use_expr { + ($($e:expr),*) => {{ $($e;)* }} + } + use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one()); } diff --git a/tests/ui/unnecessary_safety_comment.rs b/tests/ui/unnecessary_safety_comment.rs new file mode 100644 index 000000000000..7fefea7051d6 --- /dev/null +++ b/tests/ui/unnecessary_safety_comment.rs @@ -0,0 +1,51 @@ +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] +#![allow(clippy::let_unit_value, clippy::missing_safety_doc)] + +mod unsafe_items_invalid_comment { + // SAFETY: + const CONST: u32 = 0; + // SAFETY: + static STATIC: u32 = 0; + // SAFETY: + struct Struct; + // SAFETY: + enum Enum {} + // SAFETY: + mod module {} +} + +mod unnecessary_from_macro { + trait T {} + + macro_rules! no_safety_comment { + ($t:ty) => { + impl T for $t {} + }; + } + + // FIXME: This is not caught + // Safety: unnecessary + no_safety_comment!(()); + + macro_rules! with_safety_comment { + ($t:ty) => { + // Safety: unnecessary + impl T for $t {} + }; + } + + with_safety_comment!(i32); +} + +fn unnecessary_on_stmt_and_expr() -> u32 { + // SAFETY: unnecessary + let num = 42; + + // SAFETY: unnecessary + if num > 24 {} + + // SAFETY: unnecessary + 24 +} + +fn main() {} diff --git a/tests/ui/unnecessary_safety_comment.stderr b/tests/ui/unnecessary_safety_comment.stderr new file mode 100644 index 000000000000..7b2af67d64c7 --- /dev/null +++ b/tests/ui/unnecessary_safety_comment.stderr @@ -0,0 +1,115 @@ +error: constant item has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:6:5 + | +LL | const CONST: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:5:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + +error: static item has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:8:5 + | +LL | static STATIC: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:7:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: struct has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:10:5 + | +LL | struct Struct; + | ^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:9:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: enum has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:12:5 + | +LL | enum Enum {} + | ^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:11:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: module has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:14:5 + | +LL | mod module {} + | ^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:13:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: impl has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:33:13 + | +LL | impl T for $t {} + | ^^^^^^^^^^^^^^^^ +... +LL | with_safety_comment!(i32); + | ------------------------- in this macro invocation + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:32:13 + | +LL | // Safety: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expression has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:48:5 + | +LL | 24 + | ^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:47:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: statement has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:42:5 + | +LL | let num = 42; + | ^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:41:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: statement has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:45:5 + | +LL | if num > 24 {} + | ^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:44:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index fe09aad06bc8..ddeda795f817 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -2,7 +2,6 @@ #![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] -#![feature(custom_inner_attributes)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -215,14 +214,14 @@ fn get_file_path(_file_type: &FileType) -> Result, V: ?Sized>(K, PhantomData); + + impl, V: ?Sized> Key { + pub fn new(key: K) -> Key { + Key(key, PhantomData) + } + } + + pub fn pkh(pkh: &[u8]) -> Key, String> { + Key::new([b"pkh-", pkh].concat().to_vec()) + } +} + +mod issue_9771b { + #![allow(dead_code)] + + pub struct Key>(K); + + pub fn from(c: &[u8]) -> Key> { + let v = [c].concat(); + Key(v.to_vec()) + } +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 3de6d0903c0f..95d2576733cd 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -2,7 +2,6 @@ #![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] -#![feature(custom_inner_attributes)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -215,14 +214,14 @@ fn get_file_path(_file_type: &FileType) -> Result, V: ?Sized>(K, PhantomData); + + impl, V: ?Sized> Key { + pub fn new(key: K) -> Key { + Key(key, PhantomData) + } + } + + pub fn pkh(pkh: &[u8]) -> Key, String> { + Key::new([b"pkh-", pkh].concat().to_vec()) + } +} + +mod issue_9771b { + #![allow(dead_code)] + + pub struct Key>(K); + + pub fn from(c: &[u8]) -> Key> { + let v = [c].concat(); + Key(v.to_vec()) + } +} diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index 02bf45a33fbe..4918fe355986 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -1,66 +1,66 @@ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:151:64 + --> $DIR/unnecessary_to_owned.rs:150:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:151:20 + --> $DIR/unnecessary_to_owned.rs:150:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::redundant-clone` implied by `-D warnings` error: redundant clone - --> $DIR/unnecessary_to_owned.rs:152:40 + --> $DIR/unnecessary_to_owned.rs:151:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:152:21 + --> $DIR/unnecessary_to_owned.rs:151:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:153:48 + --> $DIR/unnecessary_to_owned.rs:152:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:153:19 + --> $DIR/unnecessary_to_owned.rs:152:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:154:35 + --> $DIR/unnecessary_to_owned.rs:153:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:154:18 + --> $DIR/unnecessary_to_owned.rs:153:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:155:39 + --> $DIR/unnecessary_to_owned.rs:154:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:155:20 + --> $DIR/unnecessary_to_owned.rs:154:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:60:36 + --> $DIR/unnecessary_to_owned.rs:59:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -68,415 +68,415 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:61:19 + --> $DIR/unnecessary_to_owned.rs:60:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> $DIR/unnecessary_to_owned.rs:63:20 + --> $DIR/unnecessary_to_owned.rs:62:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:64:38 + --> $DIR/unnecessary_to_owned.rs:63:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:65:20 + --> $DIR/unnecessary_to_owned.rs:64:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> $DIR/unnecessary_to_owned.rs:67:18 + --> $DIR/unnecessary_to_owned.rs:66:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:68:34 + --> $DIR/unnecessary_to_owned.rs:67:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:69:18 + --> $DIR/unnecessary_to_owned.rs:68:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:71:17 + --> $DIR/unnecessary_to_owned.rs:70:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:72:30 + --> $DIR/unnecessary_to_owned.rs:71:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:73:17 + --> $DIR/unnecessary_to_owned.rs:72:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:74:17 + --> $DIR/unnecessary_to_owned.rs:73:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:76:19 + --> $DIR/unnecessary_to_owned.rs:75:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:77:36 + --> $DIR/unnecessary_to_owned.rs:76:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:78:19 + --> $DIR/unnecessary_to_owned.rs:77:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:79:19 + --> $DIR/unnecessary_to_owned.rs:78:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:80:19 + --> $DIR/unnecessary_to_owned.rs:79:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:83:42 + --> $DIR/unnecessary_to_owned.rs:82:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:86:25 + --> $DIR/unnecessary_to_owned.rs:85:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:87:26 + --> $DIR/unnecessary_to_owned.rs:86:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:88:24 + --> $DIR/unnecessary_to_owned.rs:87:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:89:23 + --> $DIR/unnecessary_to_owned.rs:88:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:90:25 + --> $DIR/unnecessary_to_owned.rs:89:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:92:30 + --> $DIR/unnecessary_to_owned.rs:91:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:93:31 + --> $DIR/unnecessary_to_owned.rs:92:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:94:29 + --> $DIR/unnecessary_to_owned.rs:93:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:95:28 + --> $DIR/unnecessary_to_owned.rs:94:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:96:30 + --> $DIR/unnecessary_to_owned.rs:95:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:29 + --> $DIR/unnecessary_to_owned.rs:97:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:43 + --> $DIR/unnecessary_to_owned.rs:97:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:99:29 + --> $DIR/unnecessary_to_owned.rs:98:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:99:47 + --> $DIR/unnecessary_to_owned.rs:98:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:101:26 + --> $DIR/unnecessary_to_owned.rs:100:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:102:27 + --> $DIR/unnecessary_to_owned.rs:101:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:103:25 + --> $DIR/unnecessary_to_owned.rs:102:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:104:24 + --> $DIR/unnecessary_to_owned.rs:103:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:105:24 + --> $DIR/unnecessary_to_owned.rs:104:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:106:26 + --> $DIR/unnecessary_to_owned.rs:105:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:107:26 + --> $DIR/unnecessary_to_owned.rs:106:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:108:26 + --> $DIR/unnecessary_to_owned.rs:107:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:110:31 + --> $DIR/unnecessary_to_owned.rs:109:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:111:32 + --> $DIR/unnecessary_to_owned.rs:110:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:112:30 + --> $DIR/unnecessary_to_owned.rs:111:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:113:29 + --> $DIR/unnecessary_to_owned.rs:112:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:114:29 + --> $DIR/unnecessary_to_owned.rs:113:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:115:31 + --> $DIR/unnecessary_to_owned.rs:114:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:116:31 + --> $DIR/unnecessary_to_owned.rs:115:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:117:31 + --> $DIR/unnecessary_to_owned.rs:116:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:30 + --> $DIR/unnecessary_to_owned.rs:118:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:44 + --> $DIR/unnecessary_to_owned.rs:118:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:30 + --> $DIR/unnecessary_to_owned.rs:119:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:44 + --> $DIR/unnecessary_to_owned.rs:119:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:30 + --> $DIR/unnecessary_to_owned.rs:120:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:44 + --> $DIR/unnecessary_to_owned.rs:120:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:30 + --> $DIR/unnecessary_to_owned.rs:121:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:48 + --> $DIR/unnecessary_to_owned.rs:121:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:30 + --> $DIR/unnecessary_to_owned.rs:122:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:52 + --> $DIR/unnecessary_to_owned.rs:122:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:124:30 + --> $DIR/unnecessary_to_owned.rs:123:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:124:48 + --> $DIR/unnecessary_to_owned.rs:123:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:126:20 + --> $DIR/unnecessary_to_owned.rs:125:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:128:13 + --> $DIR/unnecessary_to_owned.rs:127:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:129:13 + --> $DIR/unnecessary_to_owned.rs:128:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:130:13 + --> $DIR/unnecessary_to_owned.rs:129:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:131:13 + --> $DIR/unnecessary_to_owned.rs:130:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:133:13 + --> $DIR/unnecessary_to_owned.rs:132:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:134:13 + --> $DIR/unnecessary_to_owned.rs:133:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:135:13 + --> $DIR/unnecessary_to_owned.rs:134:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:136:13 + --> $DIR/unnecessary_to_owned.rs:135:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:198:14 + --> $DIR/unnecessary_to_owned.rs:197:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -492,25 +492,25 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:221:14 + --> $DIR/unnecessary_to_owned.rs:220:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:226:14 + --> $DIR/unnecessary_to_owned.rs:225:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:273:24 + --> $DIR/unnecessary_to_owned.rs:272:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:381:12 + --> $DIR/unnecessary_to_owned.rs:380:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` diff --git a/tests/ui/doc_unnecessary_unsafe.rs b/tests/ui/unnecessary_unsafety_doc.rs similarity index 98% rename from tests/ui/doc_unnecessary_unsafe.rs rename to tests/ui/unnecessary_unsafety_doc.rs index d9e9363b0f4b..c160e31afd33 100644 --- a/tests/ui/doc_unnecessary_unsafe.rs +++ b/tests/ui/unnecessary_unsafety_doc.rs @@ -1,6 +1,7 @@ // aux-build:doc_unsafe_macros.rs #![allow(clippy::let_unit_value)] +#![warn(clippy::unnecessary_safety_doc)] #[macro_use] extern crate doc_unsafe_macros; diff --git a/tests/ui/doc_unnecessary_unsafe.stderr b/tests/ui/unnecessary_unsafety_doc.stderr similarity index 81% rename from tests/ui/doc_unnecessary_unsafe.stderr rename to tests/ui/unnecessary_unsafety_doc.stderr index 83b2efbb346b..72898c93fa11 100644 --- a/tests/ui/doc_unnecessary_unsafe.stderr +++ b/tests/ui/unnecessary_unsafety_doc.stderr @@ -1,5 +1,5 @@ error: safe function's docs have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:18:1 + --> $DIR/unnecessary_unsafety_doc.rs:19:1 | LL | pub fn apocalypse(universe: &mut ()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,31 +7,31 @@ LL | pub fn apocalypse(universe: &mut ()) { = note: `-D clippy::unnecessary-safety-doc` implied by `-D warnings` error: safe function's docs have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:44:5 + --> $DIR/unnecessary_unsafety_doc.rs:45:5 | LL | pub fn republished() { | ^^^^^^^^^^^^^^^^^^^^ error: safe function's docs have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:57:5 + --> $DIR/unnecessary_unsafety_doc.rs:58:5 | LL | fn documented(self); | ^^^^^^^^^^^^^^^^^^^^ error: docs for safe trait have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:67:1 + --> $DIR/unnecessary_unsafety_doc.rs:68:1 | LL | pub trait DocumentedSafeTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: safe function's docs have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:95:5 + --> $DIR/unnecessary_unsafety_doc.rs:96:5 | LL | pub fn documented() -> Self { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: safe function's docs have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:122:9 + --> $DIR/unnecessary_unsafety_doc.rs:123:9 | LL | pub fn drive() { | ^^^^^^^^^^^^^^ @@ -42,7 +42,7 @@ LL | very_safe!(); = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info) error: docs for safe trait have unnecessary `# Safety` section - --> $DIR/doc_unnecessary_unsafe.rs:146:1 + --> $DIR/unnecessary_unsafety_doc.rs:147:1 | LL | pub trait DocumentedSafeTraitWithImplementationHeader { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index 9786c7b12128..0a8e7b34dfa4 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![feature(box_patterns, custom_inner_attributes)] +#![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] @@ -34,14 +34,12 @@ fn main() { if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } +#[clippy::msrv = "1.52"] fn msrv_1_52() { - #![clippy::msrv = "1.52"] - if let [1] | [52] = [0] {} } +#[clippy::msrv = "1.53"] fn msrv_1_53() { - #![clippy::msrv = "1.53"] - if let [1 | 53] = [0] {} } diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index f57322396d4a..2c454adfe89d 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -1,6 +1,6 @@ // run-rustfix -#![feature(box_patterns, custom_inner_attributes)] +#![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] @@ -34,14 +34,12 @@ fn main() { if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } +#[clippy::msrv = "1.52"] fn msrv_1_52() { - #![clippy::msrv = "1.52"] - if let [1] | [52] = [0] {} } +#[clippy::msrv = "1.53"] fn msrv_1_53() { - #![clippy::msrv = "1.53"] - if let [1] | [53] = [0] {} } diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index fbc12fff0b0e..a1f193db555a 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -176,7 +176,7 @@ LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | ~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:46:12 + --> $DIR/unnested_or_patterns.rs:44:12 | LL | if let [1] | [53] = [0] {} | ^^^^^^^^^^ diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed index 38fe6c34cfec..f6f734c05ed5 100644 --- a/tests/ui/unused_rounding.fixed +++ b/tests/ui/unused_rounding.fixed @@ -11,4 +11,7 @@ fn main() { let _ = 3.3_f32.round(); let _ = 3.3_f64.round(); let _ = 3.0_f32; + + let _ = 3_3.0_0_f32; + let _ = 3_3.0_1_f64.round(); } diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs index a5cac64d023a..a0267d8144aa 100644 --- a/tests/ui/unused_rounding.rs +++ b/tests/ui/unused_rounding.rs @@ -11,4 +11,7 @@ fn main() { let _ = 3.3_f32.round(); let _ = 3.3_f64.round(); let _ = 3.0_f32.round(); + + let _ = 3_3.0_0_f32.round(); + let _ = 3_3.0_1_f64.round(); } diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr index 1eeb5d1de883..b867996fe576 100644 --- a/tests/ui/unused_rounding.stderr +++ b/tests/ui/unused_rounding.stderr @@ -24,5 +24,11 @@ error: used the `round` method with a whole number float LL | let _ = 3.0_f32.round(); | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32` -error: aborting due to 4 previous errors +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:15:13 + | +LL | let _ = 3_3.0_0_f32.round(); + | ^^^^^^^^^^^^^^^^^^^ help: remove the `round` method call: `3_3.0_0_f32` + +error: aborting due to 5 previous errors diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 3b54fe9d5ff3..0a6166571ebe 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -1,7 +1,6 @@ // run-rustfix // aux-build:proc_macro_derive.rs -#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -619,9 +618,8 @@ mod issue6902 { } } +#[clippy::msrv = "1.36"] fn msrv_1_36() { - #![clippy::msrv = "1.36"] - enum E { A, } @@ -635,9 +633,8 @@ fn msrv_1_36() { } } +#[clippy::msrv = "1.37"] fn msrv_1_37() { - #![clippy::msrv = "1.37"] - enum E { A, } diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index bf87633cd2d8..39c2b431f7fb 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -1,7 +1,6 @@ // run-rustfix // aux-build:proc_macro_derive.rs -#![feature(custom_inner_attributes)] #![warn(clippy::use_self)] #![allow(dead_code, unreachable_code)] #![allow( @@ -619,9 +618,8 @@ mod issue6902 { } } +#[clippy::msrv = "1.36"] fn msrv_1_36() { - #![clippy::msrv = "1.36"] - enum E { A, } @@ -635,9 +633,8 @@ fn msrv_1_36() { } } +#[clippy::msrv = "1.37"] fn msrv_1_37() { - #![clippy::msrv = "1.37"] - enum E { A, } diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 16fb0609242c..48364c40c3b2 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:21 + --> $DIR/use_self.rs:22:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -7,247 +7,247 @@ LL | fn new() -> Foo { = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:24:13 + --> $DIR/use_self.rs:23:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:26:22 + --> $DIR/use_self.rs:25:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:27:13 + --> $DIR/use_self.rs:26:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:32:25 + --> $DIR/use_self.rs:31:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:33:13 + --> $DIR/use_self.rs:32:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:98:24 + --> $DIR/use_self.rs:97:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:98:55 + --> $DIR/use_self.rs:97:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:113:13 + --> $DIR/use_self.rs:112:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:29 + --> $DIR/use_self.rs:147:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:149:21 + --> $DIR/use_self.rs:148:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:160:21 + --> $DIR/use_self.rs:159:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:161:13 + --> $DIR/use_self.rs:160:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:178:21 + --> $DIR/use_self.rs:177:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:179:21 + --> $DIR/use_self.rs:178:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:180:21 + --> $DIR/use_self.rs:179:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:222:13 + --> $DIR/use_self.rs:221:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:223:13 + --> $DIR/use_self.rs:222:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:225:13 + --> $DIR/use_self.rs:224:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:244:13 + --> $DIR/use_self.rs:243:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:25 + --> $DIR/use_self.rs:257:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:259:13 + --> $DIR/use_self.rs:258:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:263:16 + --> $DIR/use_self.rs:262:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:263:22 + --> $DIR/use_self.rs:262:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:286:29 + --> $DIR/use_self.rs:285:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:287:13 + --> $DIR/use_self.rs:286:13 | LL | Foo:: { value } | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:459:13 + --> $DIR/use_self.rs:458:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:496:13 + --> $DIR/use_self.rs:495:13 | LL | S2::new() | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:533:17 + --> $DIR/use_self.rs:532:17 | LL | Foo::Bar => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:534:17 + --> $DIR/use_self.rs:533:17 | LL | Foo::Baz => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:540:20 + --> $DIR/use_self.rs:539:20 | LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:564:17 + --> $DIR/use_self.rs:563:17 | LL | Something::Num(n) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:565:17 + --> $DIR/use_self.rs:564:17 | LL | Something::TupleNums(n, _m) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:566:17 + --> $DIR/use_self.rs:565:17 | LL | Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:572:17 + --> $DIR/use_self.rs:571:17 | LL | crate::issue8845::Something::Num(n) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:573:17 + --> $DIR/use_self.rs:572:17 | LL | crate::issue8845::Something::TupleNums(n, _m) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:574:17 + --> $DIR/use_self.rs:573:17 | LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:590:17 + --> $DIR/use_self.rs:589:17 | LL | let Foo(x) = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:595:17 + --> $DIR/use_self.rs:594:17 | LL | let crate::issue8845::Foo(x) = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:602:17 + --> $DIR/use_self.rs:601:17 | LL | let Bar { x, .. } = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:607:17 + --> $DIR/use_self.rs:606:17 | LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:648:17 + --> $DIR/use_self.rs:645:17 | LL | E::A => {}, | ^ help: use the applicable keyword: `Self` diff --git a/triagebot.toml b/triagebot.toml index 80c30393832c..acb476ee6962 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -4,9 +4,26 @@ allow-unauthenticated = [ "good-first-issue" ] -[assign] - # Allows shortcuts like `@rustbot ready` # # See https://github.com/rust-lang/triagebot/wiki/Shortcuts [shortcut] + +[autolabel."S-waiting-on-review"] +new_pr = true + +[assign] +contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" + +[assign.owners] +"/.github" = ["@flip1995"] +"*" = [ + "@flip1995", + "@Manishearth", + "@llogiq", + "@giraffate", + "@xFrednet", + "@Alexendoo", + "@dswij", + "@Jarcho", +] From 05477ff8df2056e7301f28930e72bd13e983504d Mon Sep 17 00:00:00 2001 From: alex-semenyuk Date: Sat, 26 Nov 2022 19:21:07 +0300 Subject: [PATCH 311/524] Fix manual_let_else produces a wrong suggestion with or-patterns --- clippy_lints/src/manual_let_else.rs | 7 ++++++- tests/ui/manual_let_else_match.rs | 7 +++++++ tests/ui/manual_let_else_match.stderr | 15 ++++++++++++--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 874d36ca9f4e..9c6f8b43c078 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -151,7 +151,12 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: } else { format!("{{ {sn_else} }}") }; - let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};"); + let sn_bl = if matches!(pat.kind, PatKind::Or(..)) { + format!("({sn_pat})") + } else { + sn_pat.into_owned() + }; + let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};"); diag.span_suggestion(span, "consider writing", sugg, app); }, ); diff --git a/tests/ui/manual_let_else_match.rs b/tests/ui/manual_let_else_match.rs index 93c86ca24fea..28caed9d79df 100644 --- a/tests/ui/manual_let_else_match.rs +++ b/tests/ui/manual_let_else_match.rs @@ -64,6 +64,13 @@ fn fire() { Ok(v) => v, Err(()) => return, }; + + let f = Variant::Bar(1); + + let _value = match f { + Variant::Bar(_) | Variant::Baz(_) => (), + _ => return, + }; } fn not_fire() { diff --git a/tests/ui/manual_let_else_match.stderr b/tests/ui/manual_let_else_match.stderr index 38be5ac54547..cd5e9a9ac39c 100644 --- a/tests/ui/manual_let_else_match.stderr +++ b/tests/ui/manual_let_else_match.stderr @@ -25,7 +25,7 @@ LL | / let v = match h() { LL | | (Some(_), Some(_)) | (None, None) => continue, LL | | (Some(v), None) | (None, Some(v)) => v, LL | | }; - | |__________^ help: consider writing: `let (Some(v), None) | (None, Some(v)) = h() else { continue };` + | |__________^ help: consider writing: `let ((Some(v), None) | (None, Some(v))) = h() else { continue };` error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:49:9 @@ -34,7 +34,7 @@ LL | / let v = match build_enum() { LL | | _ => continue, LL | | Variant::Bar(v) | Variant::Baz(v) => v, LL | | }; - | |__________^ help: consider writing: `let Variant::Bar(v) | Variant::Baz(v) = build_enum() else { continue };` + | |__________^ help: consider writing: `let (Variant::Bar(v) | Variant::Baz(v)) = build_enum() else { continue };` error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:57:5 @@ -54,5 +54,14 @@ LL | | Err(()) => return, LL | | }; | |______^ help: consider writing: `let Ok(v) = f().map_err(|_| ()) else { return };` -error: aborting due to 6 previous errors +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:70:5 + | +LL | / let _value = match f { +LL | | Variant::Bar(_) | Variant::Baz(_) => (), +LL | | _ => return, +LL | | }; + | |______^ help: consider writing: `let (Variant::Bar(_) | Variant::Baz(_)) = f else { return };` + +error: aborting due to 7 previous errors From a21b5b25f6aef692968470b9e2aae7600d1f07b4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 1 Dec 2022 12:17:38 -0500 Subject: [PATCH 312/524] Don't lint `string_lit_as_bytes` in match scrutinees --- clippy_lints/src/strings.rs | 38 +++++++++++++++++++----------- tests/ui/string_lit_as_bytes.fixed | 6 +++++ tests/ui/string_lit_as_bytes.rs | 6 +++++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index f4705481d4e6..bc18cad6d381 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{get_expr_use_or_unification_node, peel_blocks, SpanlessEq}; use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths}; -use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -249,6 +249,7 @@ const MAX_LENGTH_BYTE_STRING_LIT: usize = 32; declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]); impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use rustc_ast::LitKind; @@ -316,18 +317,27 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT && !receiver.span.from_expansion() { - span_lint_and_sugg( - cx, - STRING_LIT_AS_BYTES, - e.span, - "calling `as_bytes()` on a string literal", - "consider using a byte string literal instead", - format!( - "b{}", - snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) - ), - applicability, - ); + if let Some((parent, id)) = get_expr_use_or_unification_node(cx.tcx, e) + && let Node::Expr(parent) = parent + && let ExprKind::Match(scrutinee, ..) = parent.kind + && scrutinee.hir_id == id + { + // Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces + // `&[u8]`. This change would prevent matching with different sized slices. + } else { + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `as_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}", + snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) + ), + applicability, + ); + } } } } diff --git a/tests/ui/string_lit_as_bytes.fixed b/tests/ui/string_lit_as_bytes.fixed index df2256e4f97d..506187fc1257 100644 --- a/tests/ui/string_lit_as_bytes.fixed +++ b/tests/ui/string_lit_as_bytes.fixed @@ -25,6 +25,12 @@ fn str_lit_as_bytes() { let includestr = include_bytes!("string_lit_as_bytes.rs"); let _ = b"string with newline\t\n"; + + let _ = match "x".as_bytes() { + b"xx" => 0, + [b'x', ..] => 1, + _ => 2, + }; } fn main() {} diff --git a/tests/ui/string_lit_as_bytes.rs b/tests/ui/string_lit_as_bytes.rs index c6bf8f732ed9..2c339f1ddb81 100644 --- a/tests/ui/string_lit_as_bytes.rs +++ b/tests/ui/string_lit_as_bytes.rs @@ -25,6 +25,12 @@ fn str_lit_as_bytes() { let includestr = include_str!("string_lit_as_bytes.rs").as_bytes(); let _ = "string with newline\t\n".as_bytes(); + + let _ = match "x".as_bytes() { + b"xx" => 0, + [b'x', ..] => 1, + _ => 2, + }; } fn main() {} From 47fb67fa086d88a878c11d7edc38f322f3b98fb9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 1 Dec 2022 12:30:19 -0500 Subject: [PATCH 313/524] Don't lint `manual_assert` in `else if` --- clippy_lints/src/manual_assert.rs | 6 +++++- tests/ui/manual_assert.edition2018.fixed | 5 +++++ tests/ui/manual_assert.edition2021.fixed | 5 +++++ tests/ui/manual_assert.edition2021.stderr | 2 +- tests/ui/manual_assert.rs | 5 +++++ 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index b8ed9b9ec18f..4277455a3a21 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -2,7 +2,7 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{peel_blocks_with_stmt, span_extract_comment, sugg}; +use clippy_utils::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -47,6 +47,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { if cx.tcx.item_name(macro_call.def_id) == sym::panic; if !cx.tcx.sess.source_map().is_multiline(cond.span); if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); + // Don't change `else if foo { panic!(..) }` to `else { assert!(foo, ..) }` as it just + // shuffles the condition around. + // Should this have a config value? + if !is_else_clause(cx.tcx, expr); then { let mut applicability = Applicability::MachineApplicable; let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index c9a819ba5354..638320dd6eec 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -62,6 +62,11 @@ fn main() { panic!("panic5"); } assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } } fn issue7730(a: u8) { diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed index 2f62de51cadc..8c7e919bf62a 100644 --- a/tests/ui/manual_assert.edition2021.fixed +++ b/tests/ui/manual_assert.edition2021.fixed @@ -50,6 +50,11 @@ fn main() { assert!(!(b.is_empty() || a.is_empty()), "panic4"); assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } } fn issue7730(a: u8) { diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 237638ee1344..3555ac29243a 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -65,7 +65,7 @@ LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:73:5 + --> $DIR/manual_assert.rs:78:5 | LL | / if a > 2 { LL | | // comment diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index 6a4cc2468d41..f037c5b8405c 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -66,6 +66,11 @@ fn main() { if a.is_empty() { panic!("with expansion {}", one!()) } + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } } fn issue7730(a: u8) { From 6481d37bb9f8506362d54b216ffcabba38735aca Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 29 Nov 2022 13:35:44 +1100 Subject: [PATCH 314/524] Add `StrStyle` to `ast::LitKind::ByteStr`. This is required to distinguish between cooked and raw byte string literals in an `ast::LitKind`, without referring to an adjacent `token::Lit`. It's a prerequisite for the next commit. --- clippy_lints/src/invalid_utf8_in_unchecked.rs | 2 +- clippy_lints/src/large_include_file.rs | 2 +- clippy_lints/src/matches/match_same_arms.rs | 2 +- clippy_lints/src/utils/author.rs | 2 +- clippy_utils/src/check_proc_macro.rs | 4 +++- clippy_utils/src/consts.rs | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/invalid_utf8_in_unchecked.rs b/clippy_lints/src/invalid_utf8_in_unchecked.rs index e0a607f9a95b..6a4861747d26 100644 --- a/clippy_lints/src/invalid_utf8_in_unchecked.rs +++ b/clippy_lints/src/invalid_utf8_in_unchecked.rs @@ -33,7 +33,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidUtf8InUnchecked { if let Some([arg]) = match_function_call(cx, expr, &paths::STR_FROM_UTF8_UNCHECKED) { match &arg.kind { ExprKind::Lit(Spanned { node: lit, .. }) => { - if let LitKind::ByteStr(bytes) = &lit + if let LitKind::ByteStr(bytes, _) = &lit && std::str::from_utf8(bytes).is_err() { lint(cx, expr.span); diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 84dd61a1e4b0..424c0d9e7982 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -60,7 +60,7 @@ impl LateLintPass<'_> for LargeIncludeFile { then { let len = match &lit.node { // include_bytes - LitKind::ByteStr(bstr) => bstr.len(), + LitKind::ByteStr(bstr, _) => bstr.len(), // include_str LitKind::Str(sym, _) => sym.as_str().len(), _ => return, diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 168c1e4d2e60..158e6caa4de5 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -282,7 +282,7 @@ impl<'a> NormalizedPat<'a> { // TODO: Handle negative integers. They're currently treated as a wild match. ExprKind::Lit(lit) => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), - LitKind::ByteStr(ref bytes) => Self::LitBytes(bytes), + LitKind::ByteStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Byte(val) => Self::LitInt(val.into()), LitKind::Char(val) => Self::LitInt(val.into()), LitKind::Int(val, _) => Self::LitInt(val), diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 0c052d86eda4..bd7daf0773ca 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -299,7 +299,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }; kind!("Float(_, {float_ty})"); }, - LitKind::ByteStr(ref vec) => { + LitKind::ByteStr(ref vec, _) => { bind!(self, vec); kind!("ByteStr(ref {vec})"); chain!(self, "let [{:?}] = **{vec}", vec.value); diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index c6bf98b7b8bb..43f0df145f0e 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -69,7 +69,9 @@ fn lit_search_pat(lit: &LitKind) -> (Pat, Pat) { LitKind::Str(_, StrStyle::Cooked) => (Pat::Str("\""), Pat::Str("\"")), LitKind::Str(_, StrStyle::Raw(0)) => (Pat::Str("r"), Pat::Str("\"")), LitKind::Str(_, StrStyle::Raw(_)) => (Pat::Str("r#"), Pat::Str("#")), - LitKind::ByteStr(_) => (Pat::Str("b\""), Pat::Str("\"")), + LitKind::ByteStr(_, StrStyle::Cooked) => (Pat::Str("b\""), Pat::Str("\"")), + LitKind::ByteStr(_, StrStyle::Raw(0)) => (Pat::Str("br\""), Pat::Str("\"")), + LitKind::ByteStr(_, StrStyle::Raw(_)) => (Pat::Str("br#\""), Pat::Str("#")), LitKind::Byte(_) => (Pat::Str("b'"), Pat::Str("'")), LitKind::Char(_) => (Pat::Str("'"), Pat::Str("'")), LitKind::Int(_, LitIntType::Signed(IntTy::Isize)) => (Pat::Num, Pat::Str("isize")), diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 315aea9aa091..7a637d32babe 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -210,7 +210,7 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)), + LitKind::ByteStr(ref s, _) => Constant::Binary(Lrc::clone(s)), LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { From ab8c6beb8526f9dbf3934835681e1ff9e951ea5b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 1 Dec 2022 23:05:53 -0500 Subject: [PATCH 315/524] Don't lint `implicit_clone` when the type doesn't implement clone --- clippy_lints/src/methods/implicit_clone.rs | 4 +++- tests/ui/implicit_clone.fixed | 10 ++++++++++ tests/ui/implicit_clone.rs | 10 ++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 429cdc1918d7..06ecbce4e70e 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::peel_mid_ty_refs; +use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; use clippy_utils::{is_diag_item_method, is_diag_trait_item}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -19,6 +19,8 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv let (input_type, ref_count) = peel_mid_ty_refs(input_type); if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())); if return_type == input_type; + if let Some(clone_trait) = cx.tcx.lang_items().clone_trait(); + if implements_trait(cx, return_type, clone_trait, &[]); then { let mut app = Applicability::MachineApplicable; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; diff --git a/tests/ui/implicit_clone.fixed b/tests/ui/implicit_clone.fixed index 33770fc2a2cf..51b1afbe5ac8 100644 --- a/tests/ui/implicit_clone.fixed +++ b/tests/ui/implicit_clone.fixed @@ -115,4 +115,14 @@ fn main() { let pathbuf_ref = &pathbuf_ref; let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf` let _ = (**pathbuf_ref).clone(); + + struct NoClone; + impl ToOwned for NoClone { + type Owned = Self; + fn to_owned(&self) -> Self { + NoClone + } + } + let no_clone = &NoClone; + let _ = no_clone.to_owned(); } diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index fc896525bd27..8a9027433d95 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -115,4 +115,14 @@ fn main() { let pathbuf_ref = &pathbuf_ref; let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf` let _ = pathbuf_ref.to_path_buf(); + + struct NoClone; + impl ToOwned for NoClone { + type Owned = Self; + fn to_owned(&self) -> Self { + NoClone + } + } + let no_clone = &NoClone; + let _ = no_clone.to_owned(); } From fa4288af1fc379e5f83fb697a10b6a6758d5caf7 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 2 Dec 2022 16:26:15 +0100 Subject: [PATCH 316/524] Add missing slash to produce function documentation --- clippy_utils/src/ty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index bfb2d472a393..81b08ae5600d 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -30,7 +30,7 @@ use std::iter; use crate::{match_def_path, path_res, paths}; -// Checks if the given type implements copy. +/// Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty.is_copy_modulo_regions(cx.tcx, cx.param_env) } From 6ba2cda79a33522d89c7c89f814f2788496616b9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 1 Dec 2022 23:29:47 -0500 Subject: [PATCH 317/524] Fix `zero_ptr` suggestion for `no_std` crates --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/misc.rs | 66 +++++++++++++++++++++------------ tests/ui/zero_ptr_no_std.fixed | 21 +++++++++++ tests/ui/zero_ptr_no_std.rs | 21 +++++++++++ tests/ui/zero_ptr_no_std.stderr | 26 +++++++++++++ 5 files changed, 111 insertions(+), 25 deletions(-) create mode 100644 tests/ui/zero_ptr_no_std.fixed create mode 100644 tests/ui/zero_ptr_no_std.rs create mode 100644 tests/ui/zero_ptr_no_std.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7b17d8a156d5..11177f830abd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -538,7 +538,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool)); store.register_late_pass(|_| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); - store.register_late_pass(|_| Box::new(misc::MiscLints)); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|_| Box::new(mut_mut::MutMut)); store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 516dee20f8b1..9f4beb92b9d2 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -9,12 +9,14 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{ExpnKind, Span}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, is_integer_literal, iter_input_pats, last_path_segment, SpanlessEq}; +use clippy_utils::{ + get_parent_expr, in_constant, is_integer_literal, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq, +}; declare_clippy_lint! { /// ### What it does @@ -120,14 +122,28 @@ declare_clippy_lint! { "using `0 as *{const, mut} T`" } -declare_lint_pass!(MiscLints => [ +pub struct LintPass { + std_or_core: &'static str, +} +impl Default for LintPass { + fn default() -> Self { + Self { std_or_core: "std" } + } +} +impl_lint_pass!(LintPass => [ TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, SHORT_CIRCUIT_STATEMENT, ZERO_PTR, ]); -impl<'tcx> LateLintPass<'tcx> for MiscLints { +impl<'tcx> LateLintPass<'tcx> for LintPass { + fn check_crate(&mut self, cx: &LateContext<'_>) { + if is_no_std_crate(cx) { + self.std_or_core = "core"; + } + } + fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -231,7 +247,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Cast(e, ty) = expr.kind { - check_cast(cx, expr.span, e, ty); + self.check_cast(cx, expr.span, e, ty); return; } if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) { @@ -310,26 +326,28 @@ fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool { } } -fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { - if_chain! { - if let TyKind::Ptr(ref mut_ty) = ty.kind; - if is_integer_literal(e, 0); - if !in_constant(cx, e.hir_id); - then { - let (msg, sugg_fn) = match mut_ty.mutbl { - Mutability::Mut => ("`0 as *mut _` detected", "std::ptr::null_mut"), - Mutability::Not => ("`0 as *const _` detected", "std::ptr::null"), - }; +impl LintPass { + fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { + if_chain! { + if let TyKind::Ptr(ref mut_ty) = ty.kind; + if is_integer_literal(e, 0); + if !in_constant(cx, e.hir_id); + then { + let (msg, sugg_fn) = match mut_ty.mutbl { + Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"), + Mutability::Not => ("`0 as *const _` detected", "ptr::null"), + }; - let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { - (format!("{sugg_fn}()"), Applicability::MachineApplicable) - } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { - (format!("{sugg_fn}::<{mut_ty_snip}>()"), Applicability::MachineApplicable) - } else { - // `MaybeIncorrect` as type inference may not work with the suggested code - (format!("{sugg_fn}()"), Applicability::MaybeIncorrect) - }; - span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); + let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { + (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MachineApplicable) + } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { + (format!("{}::{sugg_fn}::<{mut_ty_snip}>()", self.std_or_core), Applicability::MachineApplicable) + } else { + // `MaybeIncorrect` as type inference may not work with the suggested code + (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MaybeIncorrect) + }; + span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); + } } } } diff --git a/tests/ui/zero_ptr_no_std.fixed b/tests/ui/zero_ptr_no_std.fixed new file mode 100644 index 000000000000..8906c776977a --- /dev/null +++ b/tests/ui/zero_ptr_no_std.fixed @@ -0,0 +1,21 @@ +// run-rustfix + +#![feature(lang_items, start, libc)] +#![no_std] +#![deny(clippy::zero_ptr)] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _ = core::ptr::null::(); + let _ = core::ptr::null_mut::(); + let _: *const u8 = core::ptr::null(); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/zero_ptr_no_std.rs b/tests/ui/zero_ptr_no_std.rs new file mode 100644 index 000000000000..379c1b18d299 --- /dev/null +++ b/tests/ui/zero_ptr_no_std.rs @@ -0,0 +1,21 @@ +// run-rustfix + +#![feature(lang_items, start, libc)] +#![no_std] +#![deny(clippy::zero_ptr)] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _ = 0 as *const usize; + let _ = 0 as *mut f64; + let _: *const u8 = 0 as *const _; + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/zero_ptr_no_std.stderr b/tests/ui/zero_ptr_no_std.stderr new file mode 100644 index 000000000000..d92bb4a6528d --- /dev/null +++ b/tests/ui/zero_ptr_no_std.stderr @@ -0,0 +1,26 @@ +error: `0 as *const _` detected + --> $DIR/zero_ptr_no_std.rs:9:13 + | +LL | let _ = 0 as *const usize; + | ^^^^^^^^^^^^^^^^^ help: try: `core::ptr::null::()` + | +note: the lint level is defined here + --> $DIR/zero_ptr_no_std.rs:5:9 + | +LL | #![deny(clippy::zero_ptr)] + | ^^^^^^^^^^^^^^^^ + +error: `0 as *mut _` detected + --> $DIR/zero_ptr_no_std.rs:10:13 + | +LL | let _ = 0 as *mut f64; + | ^^^^^^^^^^^^^ help: try: `core::ptr::null_mut::()` + +error: `0 as *const _` detected + --> $DIR/zero_ptr_no_std.rs:11:24 + | +LL | let _: *const u8 = 0 as *const _; + | ^^^^^^^^^^^^^ help: try: `core::ptr::null()` + +error: aborting due to 3 previous errors + From cb420080acf6d546c0e172ca54f0e645b3cfaf44 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 2 Dec 2022 20:39:38 -0500 Subject: [PATCH 318/524] Add test for #10021 --- tests/ui/unnecessary_to_owned.fixed | 20 ++++++++++++++++++++ tests/ui/unnecessary_to_owned.rs | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index ddeda795f817..345f6d604c4f 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -454,3 +454,23 @@ mod issue_9771b { Key(v.to_vec()) } } + +// This is a watered down version of the code in: https://github.com/oxigraph/rio +// The ICE is triggered by the call to `to_owned` on this line: +// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116 +mod issue_10021 { + #![allow(unused)] + + pub struct Iri(T); + + impl> Iri { + pub fn parse(iri: T) -> Result { + unimplemented!() + } + } + + pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> { + let base_iri = Iri::parse(url.to_owned())?; + Ok(()) + } +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 95d2576733cd..7eb53df39e5b 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -454,3 +454,23 @@ mod issue_9771b { Key(v.to_vec()) } } + +// This is a watered down version of the code in: https://github.com/oxigraph/rio +// The ICE is triggered by the call to `to_owned` on this line: +// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116 +mod issue_10021 { + #![allow(unused)] + + pub struct Iri(T); + + impl> Iri { + pub fn parse(iri: T) -> Result { + unimplemented!() + } + } + + pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> { + let base_iri = Iri::parse(url.to_owned())?; + Ok(()) + } +} From 2701a4076f3d18edc06781d7a9b3091ee5dbd05f Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 2 Dec 2022 20:41:29 -0500 Subject: [PATCH 319/524] Fix #10021 --- clippy_lints/src/methods/unnecessary_to_owned.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 17b0507682ae..9263f0519724 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -386,14 +386,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< Node::Expr(parent_expr) => { if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr) { - if Some(callee_def_id) == cx.tcx.lang_items().into_future_fn() { - return false; - } - let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder(); if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) && let Some(param_ty) = fn_sig.inputs().get(arg_index) && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind() + // https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021 + && (*param_index as usize) < call_substs.len() { if fn_sig .inputs() From eec5039f09bc7992fbacaf0778adf85dcad54997 Mon Sep 17 00:00:00 2001 From: naosense Date: Mon, 28 Nov 2022 17:40:00 +0800 Subject: [PATCH 320/524] fix test --- clippy_lints/src/indexing_slicing.rs | 2 +- .../suppress_lint_in_const/clippy.toml | 2 +- tests/ui-toml/suppress_lint_in_const/test.rs | 49 ++++++++++++- .../suppress_lint_in_const/test.stderr | 68 +++++++++++++++++-- tests/ui/indexing_slicing_index.rs | 6 +- tests/ui/indexing_slicing_index.stderr | 28 ++------ 6 files changed, 121 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 1a8ac43ac894..eebfb753a0c5 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -166,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind() { // Index is a const block. - if self.suppress_restriction_lint_in_const && let ExprKind::ConstBlock(..) = index.kind { + if let ExprKind::ConstBlock(..) = index.kind { return; } // Index is a constant uint. diff --git a/tests/ui-toml/suppress_lint_in_const/clippy.toml b/tests/ui-toml/suppress_lint_in_const/clippy.toml index d6b6fc7f2688..1b9384d7e3ee 100644 --- a/tests/ui-toml/suppress_lint_in_const/clippy.toml +++ b/tests/ui-toml/suppress_lint_in_const/clippy.toml @@ -1 +1 @@ -suppress-restriction-lint-in-const = false +suppress-restriction-lint-in-const = true diff --git a/tests/ui-toml/suppress_lint_in_const/test.rs b/tests/ui-toml/suppress_lint_in_const/test.rs index e5f4ca7cc902..5a2df9f6c5d9 100644 --- a/tests/ui-toml/suppress_lint_in_const/test.rs +++ b/tests/ui-toml/suppress_lint_in_const/test.rs @@ -1,4 +1,51 @@ +#![feature(inline_const)] #![warn(clippy::indexing_slicing)] +// We also check the out_of_bounds_indexing lint here, because it lints similar things and +// we want to avoid false positives. +#![warn(clippy::out_of_bounds_indexing)] +#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] + +const ARR: [i32; 2] = [1, 2]; +const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. +const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + +const fn idx() -> usize { + 1 +} +const fn idx4() -> usize { + 4 +} + +fn main() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + x[index]; + x[4]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + x[1 << 3]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + + x[0]; // Ok, should not produce stderr. + x[3]; // Ok, should not produce stderr. + x[const { idx() }]; // Ok, should not produce stderr. + x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + const { &ARR[idx()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + + let y = &x; + y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 + y[4]; // Ok, rustc will handle references too. + + let v = vec![0; 5]; + v[0]; + v[10]; + v[1 << 3]; + + const N: usize = 15; // Out of bounds + const M: usize = 3; // In bounds + x[N]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + x[M]; // Ok, should not produce stderr. + v[N]; + v[M]; +} /// An opaque integer representation pub struct Integer<'a> { @@ -11,5 +58,3 @@ impl<'a> Integer<'a> { self.value[0] & 0b1000_0000 != 0 } } - -fn main() {} diff --git a/tests/ui-toml/suppress_lint_in_const/test.stderr b/tests/ui-toml/suppress_lint_in_const/test.stderr index c2acfed559d0..bc178b7e1319 100644 --- a/tests/ui-toml/suppress_lint_in_const/test.stderr +++ b/tests/ui-toml/suppress_lint_in_const/test.stderr @@ -1,12 +1,70 @@ +error[E0080]: evaluation of `main::{constant#3}` failed + --> $DIR/test.rs:31:14 + | +LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +note: erroneous constant used + --> $DIR/test.rs:31:5 + | +LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + | ^^^^^^^^^^^^^^^^^^^^^^ + error: indexing may panic - --> $DIR/test.rs:11:9 + --> $DIR/test.rs:22:5 | -LL | self.value[0] & 0b1000_0000 != 0 - | ^^^^^^^^^^^^^ +LL | x[index]; + | ^^^^^^^^ | = help: consider using `.get(n)` or `.get_mut(n)` instead - = note: the suggestion might not be applicable in constant blocks = note: `-D clippy::indexing-slicing` implied by `-D warnings` -error: aborting due to previous error +error: indexing may panic + --> $DIR/test.rs:38:5 + | +LL | v[0]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:39:5 + | +LL | v[10]; + | ^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:40:5 + | +LL | v[1 << 3]; + | ^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:46:5 + | +LL | v[N]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:47:5 + | +LL | v[M]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error[E0080]: evaluation of constant value failed + --> $DIR/test.rs:10:24 + | +LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +error: aborting due to 8 previous errors +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 4476e0eb9220..26abc9edb5e4 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -6,7 +6,7 @@ #![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] const ARR: [i32; 2] = [1, 2]; -const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. +const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. const fn idx() -> usize { @@ -27,8 +27,8 @@ fn main() { x[3]; // Ok, should not produce stderr. x[const { idx() }]; // Ok, should not produce stderr. x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. - const { &ARR[idx()] }; // Ok, should not produce stderr. - const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. + const { &ARR[idx()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. + const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. let y = &x; y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 84e1f65623c3..8fd77913a3fd 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,7 +1,7 @@ error: indexing may panic --> $DIR/indexing_slicing_index.rs:9:20 | -LL | const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. +LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^ | = help: consider using `.get(n)` or `.get_mut(n)` instead @@ -20,13 +20,13 @@ LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. error[E0080]: evaluation of `main::{constant#3}` failed --> $DIR/indexing_slicing_index.rs:31:14 | -LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. +LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 note: erroneous constant used --> $DIR/indexing_slicing_index.rs:31:5 | -LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. +LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^^^^^^^^^^^^^ error: indexing may panic @@ -37,26 +37,10 @@ LL | x[index]; | = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic - --> $DIR/indexing_slicing_index.rs:28:5 - | -LL | x[const { idx() }]; // Ok, should not produce stderr. - | ^^^^^^^^^^^^^^^^^^ - | - = help: consider using `.get(n)` or `.get_mut(n)` instead - -error: indexing may panic - --> $DIR/indexing_slicing_index.rs:29:5 - | -LL | x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. - | ^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `.get(n)` or `.get_mut(n)` instead - error: indexing may panic --> $DIR/indexing_slicing_index.rs:30:14 | -LL | const { &ARR[idx()] }; // Ok, should not produce stderr. +LL | const { &ARR[idx()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^ | = help: consider using `.get(n)` or `.get_mut(n)` instead @@ -65,7 +49,7 @@ LL | const { &ARR[idx()] }; // Ok, should not produce stderr. error: indexing may panic --> $DIR/indexing_slicing_index.rs:31:14 | -LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. +LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^^ | = help: consider using `.get(n)` or `.get_mut(n)` instead @@ -117,6 +101,6 @@ error[E0080]: evaluation of constant value failed LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0080`. From 20ec2ceab8450e1a0611fbc54a17a03ffc0bd39b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 Dec 2022 11:02:10 +0100 Subject: [PATCH 321/524] Add test case for blocks with semicolon inside and outside a block --- clippy_lints/src/semicolon_block.rs | 9 ++++++--- tests/ui/semicolon_inside_block.fixed | 2 ++ tests/ui/semicolon_inside_block.rs | 2 ++ tests/ui/semicolon_outside_block.fixed | 2 ++ tests/ui/semicolon_outside_block.rs | 2 ++ 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index d3cab68137c4..8f1d1490e1f0 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -8,11 +8,13 @@ use rustc_span::Span; declare_clippy_lint! { /// ### What it does /// - /// Suggests moving the semicolon from a block inside of the block to its kast expression. + /// Suggests moving the semicolon after a block to the inside of the block, after its last + /// expression. /// /// ### Why is this bad? /// - /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine and this lint suggests inside the block. + /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine + /// and this lint suggests inside the block. /// Take a look at `semicolon_outside_block` for the other alternative. /// /// ### Example @@ -40,7 +42,8 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// - /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine and this lint suggests outside the block. + /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine + /// and this lint suggests outside the block. /// Take a look at `semicolon_inside_block` for the other alternative. /// /// ### Example diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed index 4cd112dd5e12..42e97e1ca358 100644 --- a/tests/ui/semicolon_inside_block.fixed +++ b/tests/ui/semicolon_inside_block.fixed @@ -79,5 +79,7 @@ fn main() { unit_fn_block() }; + { unit_fn_block(); }; + unit_fn_block() } diff --git a/tests/ui/semicolon_inside_block.rs b/tests/ui/semicolon_inside_block.rs index 7512125c051d..f40848f702e1 100644 --- a/tests/ui/semicolon_inside_block.rs +++ b/tests/ui/semicolon_inside_block.rs @@ -79,5 +79,7 @@ fn main() { unit_fn_block() }; + { unit_fn_block(); }; + unit_fn_block() } diff --git a/tests/ui/semicolon_outside_block.fixed b/tests/ui/semicolon_outside_block.fixed index 5bc18faaad8e..091eaa7518e9 100644 --- a/tests/ui/semicolon_outside_block.fixed +++ b/tests/ui/semicolon_outside_block.fixed @@ -79,5 +79,7 @@ fn main() { unit_fn_block() }; + { unit_fn_block(); }; + unit_fn_block() } diff --git a/tests/ui/semicolon_outside_block.rs b/tests/ui/semicolon_outside_block.rs index 0a4293238763..7ce46431fac9 100644 --- a/tests/ui/semicolon_outside_block.rs +++ b/tests/ui/semicolon_outside_block.rs @@ -79,5 +79,7 @@ fn main() { unit_fn_block() }; + { unit_fn_block(); }; + unit_fn_block() } From 00192d299d474bf211f29d61fede738aad9f5686 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 6 Dec 2022 09:47:59 +0100 Subject: [PATCH 322/524] Add beta-nominated label to triagebot Also non-maintainers should be able to label PRs with `beta-nominated`. --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index acb476ee6962..6f50ef932e11 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1,7 +1,7 @@ [relabel] allow-unauthenticated = [ "A-*", "C-*", "E-*", "I-*", "L-*", "P-*", "S-*", "T-*", - "good-first-issue" + "good-first-issue", "beta-nominated" ] # Allows shortcuts like `@rustbot ready` From f170b1f01ab9e5faabc9c9b0080065603033b4c0 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 6 Dec 2022 19:13:53 +0100 Subject: [PATCH 323/524] Use ubuntu-20.04 instead of ubuntu-latest --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index b99213011971..a0fb23e76641 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -30,7 +30,7 @@ env: jobs: base: # NOTE: If you modify this job, make sure you copy the changes to clippy_bors.yml - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: # Setup diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 6448b2d4068d..03e42e06744b 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -19,7 +19,7 @@ defaults: jobs: changelog: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master @@ -53,12 +53,12 @@ jobs: needs: changelog strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-20.04, windows-latest, macos-latest] host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc] exclude: - - os: ubuntu-latest + - os: ubuntu-20.04 host: x86_64-apple-darwin - - os: ubuntu-latest + - os: ubuntu-20.04 host: x86_64-pc-windows-msvc - os: macos-latest host: x86_64-unknown-linux-gnu @@ -147,7 +147,7 @@ jobs: metadata_collection: needs: changelog - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: # Setup @@ -166,7 +166,7 @@ jobs: integration_build: needs: changelog - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: # Setup @@ -224,7 +224,7 @@ jobs: - 'rust-lang-nursery/failure' - 'rust-lang/log' - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: # Setup @@ -265,7 +265,7 @@ jobs: end-success: name: bors test finished if: github.event.pusher.name == 'bors' && success() - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 needs: [changelog, base, metadata_collection, integration_build, integration] steps: @@ -275,7 +275,7 @@ jobs: end-failure: name: bors test finished if: github.event.pusher.name == 'bors' && (failure() || cancelled()) - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 needs: [changelog, base, metadata_collection, integration_build, integration] steps: From bb6a0aa8ed2051510ea2b6a997c2b2c9df8102e5 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Wed, 7 Dec 2022 12:29:13 +0100 Subject: [PATCH 324/524] Revert "Use ubuntu-20.04 instead of ubuntu-latest" This reverts commit f170b1f01ab9e5faabc9c9b0080065603033b4c0. --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index a0fb23e76641..b99213011971 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -30,7 +30,7 @@ env: jobs: base: # NOTE: If you modify this job, make sure you copy the changes to clippy_bors.yml - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: # Setup diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 03e42e06744b..6448b2d4068d 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -19,7 +19,7 @@ defaults: jobs: changelog: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master @@ -53,12 +53,12 @@ jobs: needs: changelog strategy: matrix: - os: [ubuntu-20.04, windows-latest, macos-latest] + os: [ubuntu-latest, windows-latest, macos-latest] host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc] exclude: - - os: ubuntu-20.04 + - os: ubuntu-latest host: x86_64-apple-darwin - - os: ubuntu-20.04 + - os: ubuntu-latest host: x86_64-pc-windows-msvc - os: macos-latest host: x86_64-unknown-linux-gnu @@ -147,7 +147,7 @@ jobs: metadata_collection: needs: changelog - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: # Setup @@ -166,7 +166,7 @@ jobs: integration_build: needs: changelog - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: # Setup @@ -224,7 +224,7 @@ jobs: - 'rust-lang-nursery/failure' - 'rust-lang/log' - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: # Setup @@ -265,7 +265,7 @@ jobs: end-success: name: bors test finished if: github.event.pusher.name == 'bors' && success() - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest needs: [changelog, base, metadata_collection, integration_build, integration] steps: @@ -275,7 +275,7 @@ jobs: end-failure: name: bors test finished if: github.event.pusher.name == 'bors' && (failure() || cancelled()) - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest needs: [changelog, base, metadata_collection, integration_build, integration] steps: From 1c03cd3fa34d6b317f33a2fd40e3d222a4d89a85 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Wed, 7 Dec 2022 12:30:13 +0100 Subject: [PATCH 325/524] Don't install dependencies on i386 --- .github/workflows/clippy_bors.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 6448b2d4068d..1bc457a94793 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -82,13 +82,6 @@ jobs: with: github_token: "${{ secrets.github_token }}" - - name: Install dependencies (Linux-i686) - run: | - sudo dpkg --add-architecture i386 - sudo apt-get update - sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386 - if: matrix.host == 'i686-unknown-linux-gnu' - - name: Checkout uses: actions/checkout@v3.0.2 From 591c18d2f01c140b55eed01ab33b96373a4ab230 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 7 Dec 2022 17:37:08 +0000 Subject: [PATCH 326/524] Add 1.58 MSRV for `collapsible_str_replace` --- clippy_lints/src/methods/mod.rs | 5 +++- clippy_utils/src/msrvs.rs | 2 +- tests/ui/collapsible_str_replace.fixed | 11 ++++++++ tests/ui/collapsible_str_replace.rs | 11 ++++++++ tests/ui/collapsible_str_replace.stderr | 34 +++++++++++++++---------- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d2913680cbb7..cb7229ba8a4b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3672,7 +3672,10 @@ impl Methods { no_effect_replace::check(cx, expr, arg1, arg2); // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint - if name == "replace" && let Some(("replace", ..)) = method_call(recv) { + if self.msrv.meets(msrvs::PATTERN_TRAIT_CHAR_ARRAY) + && name == "replace" + && let Some(("replace", ..)) = method_call(recv) + { collapsible_str_replace::check(cx, expr, arg1, arg2); } }, diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 12a512f78a69..ba5bc9c3135d 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -21,7 +21,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,65,0 { LET_ELSE } 1,62,0 { BOOL_THEN_SOME } - 1,58,0 { FORMAT_ARGS_CAPTURE } + 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } 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 } diff --git a/tests/ui/collapsible_str_replace.fixed b/tests/ui/collapsible_str_replace.fixed index 49fc9a9629e2..9792ae9ed6b8 100644 --- a/tests/ui/collapsible_str_replace.fixed +++ b/tests/ui/collapsible_str_replace.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![allow(unused)] #![warn(clippy::collapsible_str_replace)] fn get_filter() -> char { @@ -71,3 +72,13 @@ fn main() { .replace('u', iter.next().unwrap()) .replace('s', iter.next().unwrap()); } + +#[clippy::msrv = "1.57"] +fn msrv_1_57() { + let _ = "".replace('a', "1.57").replace('b', "1.57"); +} + +#[clippy::msrv = "1.58"] +fn msrv_1_58() { + let _ = "".replace(['a', 'b'], "1.58"); +} diff --git a/tests/ui/collapsible_str_replace.rs b/tests/ui/collapsible_str_replace.rs index e3e25c4146ff..baee185b79ea 100644 --- a/tests/ui/collapsible_str_replace.rs +++ b/tests/ui/collapsible_str_replace.rs @@ -1,5 +1,6 @@ // run-rustfix +#![allow(unused)] #![warn(clippy::collapsible_str_replace)] fn get_filter() -> char { @@ -74,3 +75,13 @@ fn main() { .replace('u', iter.next().unwrap()) .replace('s', iter.next().unwrap()); } + +#[clippy::msrv = "1.57"] +fn msrv_1_57() { + let _ = "".replace('a', "1.57").replace('b', "1.57"); +} + +#[clippy::msrv = "1.58"] +fn msrv_1_58() { + let _ = "".replace('a', "1.58").replace('b', "1.58"); +} diff --git a/tests/ui/collapsible_str_replace.stderr b/tests/ui/collapsible_str_replace.stderr index 8e3daf3b898a..223358cf53f3 100644 --- a/tests/ui/collapsible_str_replace.stderr +++ b/tests/ui/collapsible_str_replace.stderr @@ -1,5 +1,5 @@ error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:19:27 + --> $DIR/collapsible_str_replace.rs:20:27 | LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")` @@ -7,19 +7,19 @@ LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l"); = note: `-D clippy::collapsible-str-replace` implied by `-D warnings` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:21:27 + --> $DIR/collapsible_str_replace.rs:22:27 | LL | let _ = "hesuo worpd".replace('s', l).replace('u', l); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], l)` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:23:27 + --> $DIR/collapsible_str_replace.rs:24:27 | LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:26:10 + --> $DIR/collapsible_str_replace.rs:27:10 | LL | .replace('s', "l") | __________^ @@ -29,58 +29,64 @@ LL | | .replace('d', "l"); | |__________________________^ help: replace with: `replace(['s', 'u', 'p', 'd'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:31:27 + --> $DIR/collapsible_str_replace.rs:32:27 | LL | let _ = "hesuo world".replace(s, "l").replace('u', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:33:27 + --> $DIR/collapsible_str_replace.rs:34:27 | LL | let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:35:27 + --> $DIR/collapsible_str_replace.rs:36:27 | LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:37:27 + --> $DIR/collapsible_str_replace.rs:38:27 | LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, p], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:39:27 + --> $DIR/collapsible_str_replace.rs:40:27 | LL | let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:41:45 + --> $DIR/collapsible_str_replace.rs:42:45 | LL | let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:44:47 + --> $DIR/collapsible_str_replace.rs:45:47 | LL | let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['d', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:46:28 + --> $DIR/collapsible_str_replace.rs:47:28 | LL | let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([d, 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:48:27 + --> $DIR/collapsible_str_replace.rs:49:27 | LL | let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([get_filter(), 's'], "l")` -error: aborting due to 13 previous errors +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:86:16 + | +LL | let _ = "".replace('a', "1.58").replace('b', "1.58"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['a', 'b'], "1.58")` + +error: aborting due to 14 previous errors From 2d444a92f6f2da7f9f4c99a439485d31a9df199d Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 2 Dec 2022 20:41:29 -0500 Subject: [PATCH 327/524] Fix #10021 --- clippy_lints/src/methods/unnecessary_to_owned.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 17b0507682ae..9263f0519724 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -386,14 +386,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< Node::Expr(parent_expr) => { if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr) { - if Some(callee_def_id) == cx.tcx.lang_items().into_future_fn() { - return false; - } - let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder(); if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) && let Some(param_ty) = fn_sig.inputs().get(arg_index) && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind() + // https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021 + && (*param_index as usize) < call_substs.len() { if fn_sig .inputs() From 1f92f97e5acb3e400af406b17639cfae3926dd3b Mon Sep 17 00:00:00 2001 From: Caio Date: Thu, 8 Dec 2022 17:41:49 -0300 Subject: [PATCH 328/524] [arithmetic-side-effects]: Consider user-provided pairs --- clippy_lints/src/lib.rs | 13 +- .../src/operators/arithmetic_side_effects.rs | 74 ++++++++--- clippy_lints/src/operators/mod.rs | 3 - clippy_lints/src/utils/conf.rs | 43 +++++- .../arithmetic_side_effects_allowed.rs | 123 +++++++++++++++--- .../arithmetic_side_effects_allowed.stderr | 58 +++++++++ .../clippy.toml | 12 +- .../toml_unknown_key/conf_unknown_key.stderr | 2 + tests/ui/arithmetic_side_effects.stderr | 24 +--- 9 files changed, 286 insertions(+), 66 deletions(-) create mode 100644 tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3fe39488ab82..8facb78e35e1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -508,9 +508,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: } let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone(); + let arithmetic_side_effects_allowed_binary = conf.arithmetic_side_effects_allowed_binary.clone(); + let arithmetic_side_effects_allowed_unary = conf.arithmetic_side_effects_allowed_unary.clone(); store.register_late_pass(move |_| { Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new( - arithmetic_side_effects_allowed.clone(), + arithmetic_side_effects_allowed + .iter() + .flat_map(|el| [[el.clone(), "*".to_string()], ["*".to_string(), el.clone()]]) + .chain(arithmetic_side_effects_allowed_binary.clone()) + .collect(), + arithmetic_side_effects_allowed + .iter() + .chain(arithmetic_side_effects_allowed_unary.iter()) + .cloned() + .collect(), )) }); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 20b82d81a2ae..4fbc8398e373 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -5,25 +5,26 @@ use clippy_utils::{ peel_hir_expr_refs, }; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::{Span, Spanned}; -const HARD_CODED_ALLOWED: &[&str] = &[ - "&str", - "f32", - "f64", - "std::num::Saturating", - "std::num::Wrapping", - "std::string::String", +const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[ + ["f32", "f32"], + ["f64", "f64"], + ["std::num::Saturating", "std::num::Saturating"], + ["std::num::Wrapping", "std::num::Wrapping"], + ["std::string::String", "&str"], ]; +const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; #[derive(Debug)] pub struct ArithmeticSideEffects { - allowed: FxHashSet, + allowed_binary: FxHashMap>, + allowed_unary: FxHashSet, // Used to check whether expressions are constants, such as in enum discriminants and consts const_span: Option, expr_span: Option, @@ -33,19 +34,55 @@ impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]); impl ArithmeticSideEffects { #[must_use] - pub fn new(mut allowed: FxHashSet) -> Self { - allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from)); + pub fn new(user_allowed_binary: Vec<[String; 2]>, user_allowed_unary: Vec) -> Self { + let mut allowed_binary: FxHashMap> = <_>::default(); + for [lhs, rhs] in user_allowed_binary.into_iter().chain( + HARD_CODED_ALLOWED_BINARY + .iter() + .copied() + .map(|[lhs, rhs]| [lhs.to_string(), rhs.to_string()]), + ) { + allowed_binary.entry(lhs).or_default().insert(rhs); + } + let allowed_unary = user_allowed_unary + .into_iter() + .chain(HARD_CODED_ALLOWED_UNARY.iter().copied().map(String::from)) + .collect(); Self { - allowed, + allowed_binary, + allowed_unary, const_span: None, expr_span: None, } } - /// Checks if the given `expr` has any of the inner `allowed` elements. - fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { - self.allowed - .contains(ty.to_string().split('<').next().unwrap_or_default()) + /// Checks if the lhs and the rhs types of a binary operation like "addition" or + /// "multiplication" are present in the inner set of allowed types. + fn has_allowed_binary(&self, lhs_ty: Ty<'_>, rhs_ty: Ty<'_>) -> bool { + let lhs_ty_string = lhs_ty.to_string(); + let lhs_ty_string_elem = lhs_ty_string.split('<').next().unwrap_or_default(); + let rhs_ty_string = rhs_ty.to_string(); + let rhs_ty_string_elem = rhs_ty_string.split('<').next().unwrap_or_default(); + if let Some(rhs_from_specific) = self.allowed_binary.get(lhs_ty_string_elem) + && { + let rhs_has_allowed_ty = rhs_from_specific.contains(rhs_ty_string_elem); + rhs_has_allowed_ty || rhs_from_specific.contains("*") + } + { + true + } else if let Some(rhs_from_glob) = self.allowed_binary.get("*") { + rhs_from_glob.contains(rhs_ty_string_elem) + } else { + false + } + } + + /// Checks if the type of an unary operation like "negation" is present in the inner set of + /// allowed types. + fn has_allowed_unary(&self, ty: Ty<'_>) -> bool { + let ty_string = ty.to_string(); + let ty_string_elem = ty_string.split('<').next().unwrap_or_default(); + self.allowed_unary.contains(ty_string_elem) } // For example, 8i32 or &i64::MAX. @@ -97,8 +134,7 @@ impl ArithmeticSideEffects { }; let lhs_ty = cx.typeck_results().expr_ty(lhs); let rhs_ty = cx.typeck_results().expr_ty(rhs); - let lhs_and_rhs_have_the_same_ty = lhs_ty == rhs_ty; - if lhs_and_rhs_have_the_same_ty && self.is_allowed_ty(lhs_ty) && self.is_allowed_ty(rhs_ty) { + if self.has_allowed_binary(lhs_ty, rhs_ty) { return; } let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { @@ -137,7 +173,7 @@ impl ArithmeticSideEffects { return; } let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - if self.is_allowed_ty(ty) { + if self.has_allowed_unary(ty) { return; } let actual_un_expr = peel_hir_expr_refs(un_expr).0; diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index b8a20d5ebe9b..eba230da6c39 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -90,9 +90,6 @@ declare_clippy_lint! { /// use rust_decimal::Decimal; /// let _n = Decimal::MAX + Decimal::MAX; /// ``` - /// - /// ### Allowed types - /// Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter. #[clippy::version = "1.64.0"] pub ARITHMETIC_SIDE_EFFECTS, restriction, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b6dc8cd7ab11..d1dde069970f 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -205,10 +205,49 @@ macro_rules! define_Conf { } define_Conf! { - /// Lint: Arithmetic. + /// Lint: ARITHMETIC_SIDE_EFFECTS. /// - /// Suppress checking of the passed type names. + /// Suppress checking of the passed type names in all types of operations. + /// + /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed = ["SomeType", "AnotherType"] + /// ``` + /// + /// #### Noteworthy + /// + /// A type, say `SomeType`, listed in this configuration has the same behavior of `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. (arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet = <_>::default()), + /// Lint: ARITHMETIC_SIDE_EFFECTS. + /// + /// Suppress checking of the passed type pair names in binary operations like addition or + /// multiplication. + /// + /// Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless + /// of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`. + /// + /// Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as + /// `["AnotherType", "SomeType"]`. + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] + /// ``` + (arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default()), + /// Lint: ARITHMETIC_SIDE_EFFECTS. + /// + /// Suppress checking of the passed type names in unary operations like "negation" (`-`). + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] + /// ``` + (arithmetic_side_effects_allowed_unary: rustc_data_structures::fx::FxHashSet = <_>::default()), /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX. /// /// Suppress lints whenever the suggested change would cause breakage for other crates. diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs index e8a023ab1764..36db9e54a228 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs @@ -2,32 +2,117 @@ use core::ops::{Add, Neg}; -#[derive(Clone, Copy)] -struct Point { - x: i32, - y: i32, +macro_rules! create { + ($name:ident) => { + #[allow(clippy::arithmetic_side_effects)] + #[derive(Clone, Copy)] + struct $name; + + impl Add<$name> for $name { + type Output = $name; + fn add(self, other: $name) -> Self::Output { + todo!() + } + } + + impl Add for $name { + type Output = $name; + fn add(self, other: i32) -> Self::Output { + todo!() + } + } + + impl Add<$name> for i32 { + type Output = $name; + fn add(self, other: $name) -> Self::Output { + todo!() + } + } + + impl Add for $name { + type Output = $name; + fn add(self, other: i64) -> Self::Output { + todo!() + } + } + + impl Add<$name> for i64 { + type Output = $name; + fn add(self, other: $name) -> Self::Output { + todo!() + } + } + + impl Neg for $name { + type Output = $name; + fn neg(self) -> Self::Output { + todo!() + } + } + }; } -impl Add for Point { - type Output = Self; +create!(Foo); +create!(Bar); +create!(Baz); +create!(OutOfNames); - fn add(self, other: Self) -> Self { - todo!() - } +fn lhs_and_rhs_are_equal() { + // is explicitly on the list + let _ = OutOfNames + OutOfNames; + // is explicitly on the list + let _ = Foo + Foo; + // is implicitly on the list + let _ = Bar + Bar; + // not on the list + let _ = Baz + Baz; } -impl Neg for Point { - type Output = Self; +fn lhs_is_different() { + // is explicitly on the list + let _ = 1i32 + OutOfNames; + // is explicitly on the list + let _ = 1i32 + Foo; + // is implicitly on the list + let _ = 1i32 + Bar; + // not on the list + let _ = 1i32 + Baz; - fn neg(self) -> Self::Output { - todo!() - } + // not on the list + let _ = 1i64 + Foo; + // is implicitly on the list + let _ = 1i64 + Bar; + // not on the list + let _ = 1i64 + Baz; } -fn main() { - let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 }; +fn rhs_is_different() { + // is explicitly on the list + let _ = OutOfNames + 1i32; + // is explicitly on the list + let _ = Foo + 1i32; + // is implicitly on the list + let _ = Bar + 1i32; + // not on the list + let _ = Baz + 1i32; + + // not on the list + let _ = Foo + 1i64; + // is implicitly on the list + let _ = Bar + 1i64; + // not on the list + let _ = Baz + 1i64; +} - let point: Point = Point { x: 1, y: 0 }; - let _ = point + point; - let _ = -point; +fn unary() { + // is explicitly on the list + let _ = -OutOfNames; + // is specifically on the list + let _ = -Foo; + // not on the list + let _ = -Bar; + // not on the list + let _ = -Baz; } + +fn main() {} diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr new file mode 100644 index 000000000000..ad89534aa1b0 --- /dev/null +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr @@ -0,0 +1,58 @@ +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:68:13 + | +LL | let _ = Baz + Baz; + | ^^^^^^^^^ + | + = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:79:13 + | +LL | let _ = 1i32 + Baz; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:82:13 + | +LL | let _ = 1i64 + Foo; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:86:13 + | +LL | let _ = 1i64 + Baz; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:97:13 + | +LL | let _ = Baz + 1i32; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:100:13 + | +LL | let _ = Foo + 1i64; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:104:13 + | +LL | let _ = Baz + 1i64; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:113:13 + | +LL | let _ = -Bar; + | ^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:115:13 + | +LL | let _ = -Baz; + | ^^^^ + +error: aborting due to 9 previous errors + diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml b/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml index e736256f29a4..89cbea7ecfe4 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml +++ b/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml @@ -1 +1,11 @@ -arithmetic-side-effects-allowed = ["Point"] +arithmetic-side-effects-allowed = [ + "OutOfNames" +] +arithmetic-side-effects-allowed-binary = [ + ["Foo", "Foo"], + ["Foo", "i32"], + ["i32", "Foo"], + ["Bar", "*"], + ["*", "Bar"], +] +arithmetic-side-effects-allowed-unary = ["Foo"] diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 01a5e962c949..d8329f9c61ba 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -6,6 +6,8 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie allow-unwrap-in-tests allowed-scripts arithmetic-side-effects-allowed + arithmetic-side-effects-allowed-binary + arithmetic-side-effects-allowed-unary array-size-threshold avoid-breaking-exported-api await-holding-invalid-types diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 0259a0824e79..9fe4b7cf28d8 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,28 +1,10 @@ -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:78:13 - | -LL | let _ = String::new() + ""; - | ^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:86:27 - | -LL | let inferred_string = string + ""; - | ^^^^^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:90:13 - | -LL | let _ = inferred_string + ""; - | ^^^^^^^^^^^^^^^^^^^^ - error: arithmetic operation that can potentially result in unexpected side-effects --> $DIR/arithmetic_side_effects.rs:165:5 | LL | _n += 1; | ^^^^^^^ + | + = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects --> $DIR/arithmetic_side_effects.rs:166:5 @@ -348,5 +330,5 @@ error: arithmetic operation that can potentially result in unexpected side-effec LL | _n = -&_n; | ^^^^ -error: aborting due to 58 previous errors +error: aborting due to 55 previous errors From dc50bb0961e1033b9a0058800e563932b34b5e3a Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Sat, 3 Dec 2022 16:03:27 -0800 Subject: [PATCH 329/524] Remove unneeded field from `SwitchTargets` --- clippy_utils/src/qualify_min_const_fn.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 480e8e55cf39..e053a9dc8881 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -303,7 +303,6 @@ fn check_terminator<'tcx>( TerminatorKind::SwitchInt { discr, - switch_ty: _, targets: _, } => check_operand(tcx, discr, span, body), From 4c80f210c38b78ddce983c2db16132a37bffd829 Mon Sep 17 00:00:00 2001 From: Hannah Town Date: Fri, 9 Dec 2022 13:29:50 -0500 Subject: [PATCH 330/524] Add lint `almost_complete_range` This replaces and expands `almost_complete_letter_range`. --- CHANGELOG.md | 1 + ...tter_range.rs => almost_complete_range.rs} | 24 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 4 +- clippy_lints/src/renamed_lints.rs | 1 + tests/ui/almost_complete_letter_range.stderr | 113 --------- ...ange.fixed => almost_complete_range.fixed} | 49 +++- ...tter_range.rs => almost_complete_range.rs} | 49 +++- tests/ui/almost_complete_range.stderr | 235 ++++++++++++++++++ tests/ui/auxiliary/macro_rules.rs | 4 +- .../needless_parens_on_range_literals.fixed | 2 +- tests/ui/needless_parens_on_range_literals.rs | 2 +- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 92 +++---- 15 files changed, 390 insertions(+), 192 deletions(-) rename clippy_lints/src/{almost_complete_letter_range.rs => almost_complete_range.rs} (85%) delete mode 100644 tests/ui/almost_complete_letter_range.stderr rename tests/ui/{almost_complete_letter_range.fixed => almost_complete_range.fixed} (56%) rename tests/ui/{almost_complete_letter_range.rs => almost_complete_range.rs} (57%) create mode 100644 tests/ui/almost_complete_range.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index bc41e70fe1f3..70a09db074e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3875,6 +3875,7 @@ Released 2018-09-13 [`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core [`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason [`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range +[`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_range.rs similarity index 85% rename from clippy_lints/src/almost_complete_letter_range.rs rename to clippy_lints/src/almost_complete_range.rs index 52beaf504a4e..42e14b5cd945 100644 --- a/clippy_lints/src/almost_complete_letter_range.rs +++ b/clippy_lints/src/almost_complete_range.rs @@ -10,8 +10,8 @@ use rustc_span::Span; declare_clippy_lint! { /// ### What it does - /// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but - /// don't because they're a half open range. + /// Checks for ranges which almost include the entire range of letters from 'a' to 'z' + /// or digits from '0' to '9', but don't because they're a half open range. /// /// ### Why is this bad? /// This (`'a'..'z'`) is almost certainly a typo meant to include all letters. @@ -25,21 +25,21 @@ declare_clippy_lint! { /// let _ = 'a'..='z'; /// ``` #[clippy::version = "1.63.0"] - pub ALMOST_COMPLETE_LETTER_RANGE, + pub ALMOST_COMPLETE_RANGE, suspicious, - "almost complete letter range" + "almost complete range" } -impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]); +impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]); -pub struct AlmostCompleteLetterRange { +pub struct AlmostCompleteRange { msrv: Msrv, } -impl AlmostCompleteLetterRange { +impl AlmostCompleteRange { pub fn new(msrv: Msrv) -> Self { Self { msrv } } } -impl EarlyLintPass for AlmostCompleteLetterRange { +impl EarlyLintPass for AlmostCompleteRange { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind { let ctxt = e.span.ctxt(); @@ -87,14 +87,18 @@ fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg Ok(LitKind::Byte(b'A') | LitKind::Char('A')), Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')), ) + | ( + Ok(LitKind::Byte(b'0') | LitKind::Char('0')), + Ok(LitKind::Byte(b'9') | LitKind::Char('9')), + ) ) && !in_external_macro(cx.sess(), span) { span_lint_and_then( cx, - ALMOST_COMPLETE_LETTER_RANGE, + ALMOST_COMPLETE_RANGE, span, - "almost complete ascii letter range", + "almost complete ascii range", |diag| { if let Some((span, sugg)) = sugg { diag.span_suggestion( diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 77c1da993dcc..3cd7d1d7e722 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -35,7 +35,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, - crate::almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE_INFO, + crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, crate::approx_const::APPROX_CONSTANT_INFO, crate::as_conversions::AS_CONVERSIONS_INFO, crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bea28f7620b8..39850d598038 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -66,7 +66,7 @@ mod declared_lints; mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` -mod almost_complete_letter_range; +mod almost_complete_range; mod approx_const; mod as_conversions; mod asm_syntax; @@ -876,7 +876,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); - store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv()))); + store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(msrv()))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)); store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)); diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 8e214218f23a..72c25592609b 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -2,6 +2,7 @@ #[rustfmt::skip] pub static RENAMED_LINTS: &[(&str, &str)] = &[ + ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), ("clippy::blacklisted_name", "clippy::disallowed_names"), ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"), ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"), diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr deleted file mode 100644 index 9abf6d6c5e7d..000000000000 --- a/tests/ui/almost_complete_letter_range.stderr +++ /dev/null @@ -1,113 +0,0 @@ -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:29:17 - | -LL | let _ = ('a') ..'z'; - | ^^^^^^--^^^ - | | - | help: use an inclusive range: `..=` - | - = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:30:17 - | -LL | let _ = 'A' .. ('Z'); - | ^^^^--^^^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:36:13 - | -LL | let _ = (b'a')..(b'z'); - | ^^^^^^--^^^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:37:13 - | -LL | let _ = b'A'..b'Z'; - | ^^^^--^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:42:13 - | -LL | let _ = a!()..'z'; - | ^^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:45:9 - | -LL | b'a'..b'z' if true => 1, - | ^^^^--^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:46:9 - | -LL | b'A'..b'Z' if true => 2, - | ^^^^--^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:53:9 - | -LL | 'a'..'z' if true => 1, - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:54:9 - | -LL | 'A'..'Z' if true => 2, - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:22:17 - | -LL | let _ = 'a'..'z'; - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` -... -LL | b!(); - | ---- in this macro invocation - | - = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:67:9 - | -LL | 'a'..'z' => 1, - | ^^^--^^^ - | | - | help: use an inclusive range: `...` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:74:13 - | -LL | let _ = 'a'..'z'; - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:76:9 - | -LL | 'a'..'z' => 1, - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: aborting due to 13 previous errors - diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_range.fixed similarity index 56% rename from tests/ui/almost_complete_letter_range.fixed rename to tests/ui/almost_complete_range.fixed index adcbd4d5134d..6046addf7196 100644 --- a/tests/ui/almost_complete_letter_range.fixed +++ b/tests/ui/almost_complete_range.fixed @@ -4,9 +4,10 @@ #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] -#![warn(clippy::almost_complete_letter_range)] +#![warn(clippy::almost_complete_range)] #![allow(ellipsis_inclusive_range_patterns)] #![allow(clippy::needless_parens_on_range_literals)] +#![allow(clippy::double_parens)] #[macro_use] extern crate macro_rules; @@ -16,10 +17,22 @@ macro_rules! a { 'a' }; } +macro_rules! A { + () => { + 'A' + }; +} +macro_rules! zero { + () => { + '0' + }; +} macro_rules! b { () => { let _ = 'a'..='z'; + let _ = 'A'..='Z'; + let _ = '0'..='9'; }; } @@ -28,36 +41,46 @@ fn main() { { let _ = ('a') ..='z'; let _ = 'A' ..= ('Z'); + let _ = ((('0'))) ..= ('9'); } let _ = 'b'..'z'; let _ = 'B'..'Z'; + let _ = '1'..'9'; let _ = (b'a')..=(b'z'); let _ = b'A'..=b'Z'; + let _ = b'0'..=b'9'; let _ = b'b'..b'z'; let _ = b'B'..b'Z'; + let _ = b'1'..b'9'; let _ = a!()..='z'; + let _ = A!()..='Z'; + let _ = zero!()..='9'; let _ = match 0u8 { b'a'..=b'z' if true => 1, b'A'..=b'Z' if true => 2, - b'b'..b'z' => 3, - b'B'..b'Z' => 4, - _ => 5, + b'0'..=b'9' if true => 3, + b'b'..b'z' => 4, + b'B'..b'Z' => 5, + b'1'..b'9' => 6, + _ => 7, }; let _ = match 'x' { 'a'..='z' if true => 1, 'A'..='Z' if true => 2, - 'b'..'z' => 3, - 'B'..'Z' => 4, - _ => 5, + '0'..='9' if true => 3, + 'b'..'z' => 4, + 'B'..'Z' => 5, + '1'..'9' => 6, + _ => 7, }; - almost_complete_letter_range!(); + almost_complete_range!(); b!(); } @@ -65,15 +88,21 @@ fn main() { fn _under_msrv() { let _ = match 'a' { 'a'...'z' => 1, - _ => 2, + 'A'...'Z' => 2, + '0'...'9' => 3, + _ => 4, }; } #[clippy::msrv = "1.26"] fn _meets_msrv() { let _ = 'a'..='z'; + let _ = 'A'..='Z'; + let _ = '0'..='9'; let _ = match 'a' { 'a'..='z' => 1, - _ => 2, + 'A'..='Z' => 1, + '0'..='9' => 3, + _ => 4, }; } diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_range.rs similarity index 57% rename from tests/ui/almost_complete_letter_range.rs rename to tests/ui/almost_complete_range.rs index 9979316eca42..ae7e07ab872b 100644 --- a/tests/ui/almost_complete_letter_range.rs +++ b/tests/ui/almost_complete_range.rs @@ -4,9 +4,10 @@ #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] -#![warn(clippy::almost_complete_letter_range)] +#![warn(clippy::almost_complete_range)] #![allow(ellipsis_inclusive_range_patterns)] #![allow(clippy::needless_parens_on_range_literals)] +#![allow(clippy::double_parens)] #[macro_use] extern crate macro_rules; @@ -16,10 +17,22 @@ macro_rules! a { 'a' }; } +macro_rules! A { + () => { + 'A' + }; +} +macro_rules! zero { + () => { + '0' + }; +} macro_rules! b { () => { let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; }; } @@ -28,36 +41,46 @@ fn main() { { let _ = ('a') ..'z'; let _ = 'A' .. ('Z'); + let _ = ((('0'))) .. ('9'); } let _ = 'b'..'z'; let _ = 'B'..'Z'; + let _ = '1'..'9'; let _ = (b'a')..(b'z'); let _ = b'A'..b'Z'; + let _ = b'0'..b'9'; let _ = b'b'..b'z'; let _ = b'B'..b'Z'; + let _ = b'1'..b'9'; let _ = a!()..'z'; + let _ = A!()..'Z'; + let _ = zero!()..'9'; let _ = match 0u8 { b'a'..b'z' if true => 1, b'A'..b'Z' if true => 2, - b'b'..b'z' => 3, - b'B'..b'Z' => 4, - _ => 5, + b'0'..b'9' if true => 3, + b'b'..b'z' => 4, + b'B'..b'Z' => 5, + b'1'..b'9' => 6, + _ => 7, }; let _ = match 'x' { 'a'..'z' if true => 1, 'A'..'Z' if true => 2, - 'b'..'z' => 3, - 'B'..'Z' => 4, - _ => 5, + '0'..'9' if true => 3, + 'b'..'z' => 4, + 'B'..'Z' => 5, + '1'..'9' => 6, + _ => 7, }; - almost_complete_letter_range!(); + almost_complete_range!(); b!(); } @@ -65,15 +88,21 @@ fn main() { fn _under_msrv() { let _ = match 'a' { 'a'..'z' => 1, - _ => 2, + 'A'..'Z' => 2, + '0'..'9' => 3, + _ => 4, }; } #[clippy::msrv = "1.26"] fn _meets_msrv() { let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; let _ = match 'a' { 'a'..'z' => 1, - _ => 2, + 'A'..'Z' => 1, + '0'..'9' => 3, + _ => 4, }; } diff --git a/tests/ui/almost_complete_range.stderr b/tests/ui/almost_complete_range.stderr new file mode 100644 index 000000000000..a7a532878502 --- /dev/null +++ b/tests/ui/almost_complete_range.stderr @@ -0,0 +1,235 @@ +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:42:17 + | +LL | let _ = ('a') ..'z'; + | ^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` + | + = note: `-D clippy::almost-complete-range` implied by `-D warnings` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:43:17 + | +LL | let _ = 'A' .. ('Z'); + | ^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:44:17 + | +LL | let _ = ((('0'))) .. ('9'); + | ^^^^^^^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:51:13 + | +LL | let _ = (b'a')..(b'z'); + | ^^^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:52:13 + | +LL | let _ = b'A'..b'Z'; + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:53:13 + | +LL | let _ = b'0'..b'9'; + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:59:13 + | +LL | let _ = a!()..'z'; + | ^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:60:13 + | +LL | let _ = A!()..'Z'; + | ^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:61:13 + | +LL | let _ = zero!()..'9'; + | ^^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:64:9 + | +LL | b'a'..b'z' if true => 1, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:65:9 + | +LL | b'A'..b'Z' if true => 2, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:66:9 + | +LL | b'0'..b'9' if true => 3, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:74:9 + | +LL | 'a'..'z' if true => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:75:9 + | +LL | 'A'..'Z' if true => 2, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:76:9 + | +LL | '0'..'9' if true => 3, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:33:17 + | +LL | let _ = 'a'..'z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:34:17 + | +LL | let _ = 'A'..'Z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:35:17 + | +LL | let _ = '0'..'9'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:90:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:91:9 + | +LL | 'A'..'Z' => 2, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:92:9 + | +LL | '0'..'9' => 3, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:99:13 + | +LL | let _ = 'a'..'z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:100:13 + | +LL | let _ = 'A'..'Z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:101:13 + | +LL | let _ = '0'..'9'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:103:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:104:9 + | +LL | 'A'..'Z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:105:9 + | +LL | '0'..'9' => 3, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: aborting due to 27 previous errors + diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index ef3ca9aea380..1e5f20e8c39b 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -142,8 +142,10 @@ macro_rules! equatable_if_let { } #[macro_export] -macro_rules! almost_complete_letter_range { +macro_rules! almost_complete_range { () => { let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; }; } diff --git a/tests/ui/needless_parens_on_range_literals.fixed b/tests/ui/needless_parens_on_range_literals.fixed index 1bd75c806bc9..f11330a8916d 100644 --- a/tests/ui/needless_parens_on_range_literals.fixed +++ b/tests/ui/needless_parens_on_range_literals.fixed @@ -2,7 +2,7 @@ // edition:2018 #![warn(clippy::needless_parens_on_range_literals)] -#![allow(clippy::almost_complete_letter_range)] +#![allow(clippy::almost_complete_range)] fn main() { let _ = 'a'..='z'; diff --git a/tests/ui/needless_parens_on_range_literals.rs b/tests/ui/needless_parens_on_range_literals.rs index 7abb8a1adc1b..671c0009e23b 100644 --- a/tests/ui/needless_parens_on_range_literals.rs +++ b/tests/ui/needless_parens_on_range_literals.rs @@ -2,7 +2,7 @@ // edition:2018 #![warn(clippy::needless_parens_on_range_literals)] -#![allow(clippy::almost_complete_letter_range)] +#![allow(clippy::almost_complete_range)] fn main() { let _ = ('a')..=('z'); diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 689928f04794..2f76b5752960 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -4,6 +4,7 @@ // run-rustfix +#![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_if_conditions)] #![allow(clippy::box_collection)] @@ -37,6 +38,7 @@ #![allow(temporary_cstring_as_ptr)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![warn(clippy::almost_complete_range)] #![warn(clippy::disallowed_names)] #![warn(clippy::blocks_in_if_conditions)] #![warn(clippy::blocks_in_if_conditions)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index b74aa650ffd4..699c0ff464e9 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -4,6 +4,7 @@ // run-rustfix +#![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_if_conditions)] #![allow(clippy::box_collection)] @@ -37,6 +38,7 @@ #![allow(temporary_cstring_as_ptr)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![warn(clippy::almost_complete_letter_range)] #![warn(clippy::blacklisted_name)] #![warn(clippy::block_in_if_condition_expr)] #![warn(clippy::block_in_if_condition_stmt)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 622a32c5908a..9af58dc75a68 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,244 +1,250 @@ +error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` + --> $DIR/rename.rs:41:9 + | +LL | #![warn(clippy::almost_complete_letter_range)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:40:9 + --> $DIR/rename.rs:42:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:41:9 + --> $DIR/rename.rs:43:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:42:9 + --> $DIR/rename.rs:44:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:43:9 + --> $DIR/rename.rs:45:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:44:9 + --> $DIR/rename.rs:46:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:45:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:46:9 + --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:47:9 + --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:48:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:49:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 40 previous errors +error: aborting due to 41 previous errors From e5010c996e9cb9fa09fefb4e3d31608915869c78 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 10 Dec 2022 18:35:24 +0900 Subject: [PATCH 331/524] uninlined_format_args: Ignore assert! and debug_assert! before 2021 edition --- clippy_lints/src/format_args.rs | 8 +++--- clippy_utils/src/macros.rs | 6 +++++ ...nlined_format_args_panic.edition2018.fixed | 3 +++ ...nlined_format_args_panic.edition2021.fixed | 3 +++ ...lined_format_args_panic.edition2021.stderr | 26 ++++++++++++++++++- tests/ui/uninlined_format_args_panic.rs | 3 +++ 6 files changed, 45 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index f0995a81329d..aa182a3230bb 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred}; use clippy_utils::macros::{ - is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage, + is_assert_macro, is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, + FormatParamUsage, }; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; @@ -290,8 +291,9 @@ fn check_uninlined_args( if args.format_string.span.from_expansion() { return; } - if call_site.edition() < Edition2021 && is_panic(cx, def_id) { - // panic! before 2021 edition considers a single string argument as non-format + if call_site.edition() < Edition2021 && (is_panic(cx, def_id) || is_assert_macro(cx, def_id)) { + // panic!, assert!, and debug_assert! before 2021 edition considers a single string argument as + // non-format return; } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index d13b34a66cca..77c5f1155423 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -208,6 +208,12 @@ pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool { ) } +/// Is `def_id` of `assert!` or `debug_assert!` +pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool { + let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false }; + matches!(name, sym::assert_macro | sym::debug_assert_macro) +} + pub enum PanicExpn<'a> { /// No arguments - `panic!()` Empty, diff --git a/tests/ui/uninlined_format_args_panic.edition2018.fixed b/tests/ui/uninlined_format_args_panic.edition2018.fixed index 96cc0877960e..52b5343c351e 100644 --- a/tests/ui/uninlined_format_args_panic.edition2018.fixed +++ b/tests/ui/uninlined_format_args_panic.edition2018.fixed @@ -26,4 +26,7 @@ fn main() { panic!("p4 {var}"); } } + + assert!(var == 1, "p5 {}", var); + debug_assert!(var == 1, "p6 {}", var); } diff --git a/tests/ui/uninlined_format_args_panic.edition2021.fixed b/tests/ui/uninlined_format_args_panic.edition2021.fixed index faf8ca4d3a79..ee72065e28ab 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.fixed +++ b/tests/ui/uninlined_format_args_panic.edition2021.fixed @@ -26,4 +26,7 @@ fn main() { panic!("p4 {var}"); } } + + assert!(var == 1, "p5 {var}"); + debug_assert!(var == 1, "p6 {var}"); } diff --git a/tests/ui/uninlined_format_args_panic.edition2021.stderr b/tests/ui/uninlined_format_args_panic.edition2021.stderr index 0f09c45f4132..fc7b125080e7 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.stderr +++ b/tests/ui/uninlined_format_args_panic.edition2021.stderr @@ -47,5 +47,29 @@ LL - panic!("p3 {var}", var = var); LL + panic!("p3 {var}"); | -error: aborting due to 4 previous errors +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:30:5 + | +LL | assert!(var == 1, "p5 {}", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - assert!(var == 1, "p5 {}", var); +LL + assert!(var == 1, "p5 {var}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:31:5 + | +LL | debug_assert!(var == 1, "p6 {}", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - debug_assert!(var == 1, "p6 {}", var); +LL + debug_assert!(var == 1, "p6 {var}"); + | + +error: aborting due to 6 previous errors diff --git a/tests/ui/uninlined_format_args_panic.rs b/tests/ui/uninlined_format_args_panic.rs index 6421c5bbed2f..b4a0a0f496e4 100644 --- a/tests/ui/uninlined_format_args_panic.rs +++ b/tests/ui/uninlined_format_args_panic.rs @@ -26,4 +26,7 @@ fn main() { panic!("p4 {var}"); } } + + assert!(var == 1, "p5 {}", var); + debug_assert!(var == 1, "p6 {}", var); } From 055f349670155d88ae3a2f0a40baa47f524efa88 Mon Sep 17 00:00:00 2001 From: koka Date: Sat, 10 Dec 2022 21:05:08 +0900 Subject: [PATCH 332/524] Avoid `match_wildcard_for_single_variants` on guarded wild matches fix #9993 changlog: [`match_wildcard_for_single_variants`] avoid suggestion on wildcard with guard --- clippy_lints/src/matches/match_wild_enum.rs | 2 +- clippy_utils/src/sugg.rs | 1 - .../ui/match_wildcard_for_single_variants.fixed | 16 ++++++++++++++++ tests/ui/match_wildcard_for_single_variants.rs | 16 ++++++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 7f8d124838cb..59de8c0384ba 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -30,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let mut has_non_wild = false; for arm in arms { match peel_hir_pat_refs(arm.pat).0.kind { - PatKind::Wild => wildcard_span = Some(arm.pat.span), + PatKind::Wild if arm.guard.is_none() => wildcard_span = Some(arm.pat.span), PatKind::Binding(_, _, ident, None) => { wildcard_span = Some(arm.pat.span); wildcard_ident = Some(ident); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index b66604f33db1..4c4c077d771f 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -185,7 +185,6 @@ impl<'a> Sugg<'a> { ) -> Self { use rustc_ast::ast::RangeLimits; - #[expect(clippy::match_wildcard_for_single_variants)] match expr.kind { _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::AddrOf(..) diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index e675c183ea71..c508b7cc3b2a 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -132,3 +132,19 @@ fn main() { } } } + +mod issue9993 { + enum Foo { + A(bool), + B, + } + + fn test() { + let _ = match Foo::A(true) { + _ if false => 0, + Foo::A(true) => 1, + Foo::A(false) => 2, + Foo::B => 3, + }; + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 38c3ffc00c71..ad03f7971297 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -132,3 +132,19 @@ fn main() { } } } + +mod issue9993 { + enum Foo { + A(bool), + B, + } + + fn test() { + let _ = match Foo::A(true) { + _ if false => 0, + Foo::A(true) => 1, + Foo::A(false) => 2, + Foo::B => 3, + }; + } +} From 55f1698a6caa8ed4f74fa4decb918cbf173d7a44 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Sat, 10 Dec 2022 19:15:08 +0300 Subject: [PATCH 333/524] Fix badge --- book/src/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/README.md b/book/src/README.md index 6248d588a890..23867df8efe1 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -1,6 +1,6 @@ # Clippy -[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto) +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test%20(bors)/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto) [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license) A collection of lints to catch common mistakes and improve your From 3b6bbf7d16dd8b42fadf8dcdbad4d4d872a3e88c Mon Sep 17 00:00:00 2001 From: alex-semenyuk Date: Mon, 7 Nov 2022 10:51:52 +0300 Subject: [PATCH 334/524] Fix match_single_binding suggestion introduced an extra semicolon --- .../src/matches/match_single_binding.rs | 18 ++++--------- tests/ui/match_single_binding.fixed | 13 +++++++++ tests/ui/match_single_binding.rs | 14 ++++++++++ tests/ui/match_single_binding.stderr | 27 ++++++++++++++++++- 4 files changed, 58 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 1bf8d4e96ad4..c94a1f763306 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -31,19 +31,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e }; // Do we need to add ';' to suggestion ? - match match_body.kind { - ExprKind::Block(block, _) => { - // macro + expr_ty(body) == () - if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { - snippet_body.push(';'); - } - }, - _ => { - // expr_ty(body) == () - if cx.typeck_results().expr_ty(match_body).is_unit() { - snippet_body.push(';'); - } - }, + if let ExprKind::Block(block, _) = match_body.kind { + // macro + expr_ty(body) == () + if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { + snippet_body.push(';'); + } } let mut applicability = Applicability::MaybeIncorrect; diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index a6e315e4773a..6cfb6661a039 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -133,3 +133,16 @@ fn issue_9575() { println!("Needs curlies"); }; } + +#[allow(dead_code)] +fn issue_9725(r: Option) { + let x = r; + match x { + Some(_) => { + println!("Some"); + }, + None => { + println!("None"); + }, + }; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index cecbd703e566..f188aeb5f2ff 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -148,3 +148,17 @@ fn issue_9575() { _ => println!("Needs curlies"), }; } + +#[allow(dead_code)] +fn issue_9725(r: Option) { + match r { + x => match x { + Some(_) => { + println!("Some"); + }, + None => { + println!("None"); + }, + }, + }; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 2b9ec7ee7026..e960d64ad2b0 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -213,5 +213,30 @@ LL + println!("Needs curlies"); LL ~ }; | -error: aborting due to 14 previous errors +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:154:5 + | +LL | / match r { +LL | | x => match x { +LL | | Some(_) => { +LL | | println!("Some"); +... | +LL | | }, +LL | | }; + | |_____^ + | +help: consider using a `let` statement + | +LL ~ let x = r; +LL + match x { +LL + Some(_) => { +LL + println!("Some"); +LL + }, +LL + None => { +LL + println!("None"); +LL + }, +LL ~ }; + | + +error: aborting due to 15 previous errors From 25c9718c04e43b5aa18b6345e0c384f9d1236e2c Mon Sep 17 00:00:00 2001 From: naosense Date: Fri, 9 Dec 2022 11:40:50 +0800 Subject: [PATCH 335/524] check ranges with .contains calls --- clippy_lints/src/manual_is_ascii_check.rs | 75 +++++++++++++---------- tests/ui/manual_is_ascii_check.fixed | 1 + tests/ui/manual_is_ascii_check.rs | 1 + tests/ui/manual_is_ascii_check.stderr | 16 +++-- 4 files changed, 54 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 5ab049d8d133..39e7145b4792 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,11 +1,11 @@ use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, source::snippet}; +use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet}; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{def_id::DefId, sym}; +use rustc_span::{def_id::DefId, sym, Span}; declare_clippy_lint! { /// ### What it does @@ -75,47 +75,54 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { return; } - let Some(macro_call) = root_macro_call(expr.span) else { return }; - - if is_matches_macro(cx, macro_call.def_id) { + if let Some(macro_call) = root_macro_call(expr.span) + && is_matches_macro(cx, macro_call.def_id) { if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { let range = check_pat(&arm.pat.kind); - - if let Some(sugg) = match range { - CharRange::UpperChar => Some("is_ascii_uppercase"), - CharRange::LowerChar => Some("is_ascii_lowercase"), - CharRange::FullChar => Some("is_ascii_alphabetic"), - CharRange::Digit => Some("is_ascii_digit"), - CharRange::Otherwise => None, - } { - let default_snip = ".."; - // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for - // macro span, so we check applicability manually by comparing `recv` is not default. - let recv = snippet(cx, recv.span, default_snip); - - let applicability = if recv == default_snip { - Applicability::HasPlaceholders - } else { - Applicability::MachineApplicable - }; - - span_lint_and_sugg( - cx, - MANUAL_IS_ASCII_CHECK, - macro_call.span, - "manual check for common ascii range", - "try", - format!("{recv}.{sugg}()"), - applicability, - ); - } + check_is_ascii(cx, macro_call.span, recv, &range); } + } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind + && path.ident.name == sym!(contains) + && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(receiver) { + let range = check_range(start, end); + check_is_ascii(cx, expr.span, arg, &range); } } extract_msrv_attr!(LateContext); } +fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &CharRange) { + if let Some(sugg) = match range { + CharRange::UpperChar => Some("is_ascii_uppercase"), + CharRange::LowerChar => Some("is_ascii_lowercase"), + CharRange::FullChar => Some("is_ascii_alphabetic"), + CharRange::Digit => Some("is_ascii_digit"), + CharRange::Otherwise => None, + } { + let default_snip = ".."; + // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for + // macro span, so we check applicability manually by comparing `recv` is not default. + let recv = snippet(cx, recv.span, default_snip); + + let applicability = if recv == default_snip { + Applicability::HasPlaceholders + } else { + Applicability::MachineApplicable + }; + + span_lint_and_sugg( + cx, + MANUAL_IS_ASCII_CHECK, + span, + "manual check for common ascii range", + "try", + format!("{recv}.{sugg}()"), + applicability, + ); + } +} + fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { match pat_kind { PatKind::Or(pats) => { diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index 231ba83b1426..bfba6dd7cd8d 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -15,6 +15,7 @@ fn main() { assert!('x'.is_ascii_alphabetic()); assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); + assert!(&b'0'.is_ascii_digit()); } #[clippy::msrv = "1.23"] diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index 39ee6151c56f..c929f30f729e 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -15,6 +15,7 @@ fn main() { assert!(matches!('x', 'A'..='Z' | 'a'..='z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); + assert!((b'0'..=b'9').contains(&b'0')); } #[clippy::msrv = "1.23"] diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index 397cbe05c822..888924f93861 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -43,28 +43,34 @@ LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:29:13 + --> $DIR/manual_is_ascii_check.rs:18:13 + | +LL | assert!((b'0'..=b'9').contains(&b'0')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&b'0'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:30:13 | LL | assert!(matches!(b'1', b'0'..=b'9')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:30:13 + --> $DIR/manual_is_ascii_check.rs:31:13 | LL | assert!(matches!('X', 'A'..='Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:31:13 + --> $DIR/manual_is_ascii_check.rs:32:13 | LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:41:23 + --> $DIR/manual_is_ascii_check.rs:42:23 | LL | const FOO: bool = matches!('x', '0'..='9'); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From de92da297466f974b63fb8157419c0616cbfd558 Mon Sep 17 00:00:00 2001 From: naosense Date: Mon, 12 Dec 2022 18:58:02 +0800 Subject: [PATCH 336/524] add more test, limits check --- clippy_lints/src/manual_is_ascii_check.rs | 4 ++- tests/ui/manual_is_ascii_check.fixed | 7 ++++ tests/ui/manual_is_ascii_check.rs | 7 ++++ tests/ui/manual_is_ascii_check.stderr | 42 +++++++++++++++++++---- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 39e7145b4792..eaaaf0c65812 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,5 +1,6 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet}; +use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; @@ -83,7 +84,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { } } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind && path.ident.name == sym!(contains) - && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(receiver) { + && let Some(higher::Range { start: Some(start), end: Some(end), limits: RangeLimits::Closed }) + = higher::Range::hir(receiver) { let range = check_range(start, end); check_is_ascii(cx, expr.span, arg, &range); } diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index bfba6dd7cd8d..b2f45aba59aa 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -15,7 +15,14 @@ fn main() { assert!('x'.is_ascii_alphabetic()); assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); + assert!(&b'0'.is_ascii_digit()); + assert!(&b'a'.is_ascii_lowercase()); + assert!(&b'A'.is_ascii_uppercase()); + + assert!(&'0'.is_ascii_digit()); + assert!(&'a'.is_ascii_lowercase()); + assert!(&'A'.is_ascii_uppercase()); } #[clippy::msrv = "1.23"] diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index c929f30f729e..7f1ee88fc743 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -15,7 +15,14 @@ fn main() { assert!(matches!('x', 'A'..='Z' | 'a'..='z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); + assert!((b'0'..=b'9').contains(&b'0')); + assert!((b'a'..=b'z').contains(&b'a')); + assert!((b'A'..=b'Z').contains(&b'A')); + + assert!(('0'..='9').contains(&'0')); + assert!(('a'..='z').contains(&'a')); + assert!(('A'..='Z').contains(&'A')); } #[clippy::msrv = "1.23"] diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index 888924f93861..797952a3aba5 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -43,34 +43,64 @@ LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:18:13 + --> $DIR/manual_is_ascii_check.rs:19:13 | LL | assert!((b'0'..=b'9').contains(&b'0')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&b'0'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:30:13 + --> $DIR/manual_is_ascii_check.rs:20:13 + | +LL | assert!((b'a'..=b'z').contains(&b'a')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&b'a'.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:21:13 + | +LL | assert!((b'A'..=b'Z').contains(&b'A')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&b'A'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:23:13 + | +LL | assert!(('0'..='9').contains(&'0')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'0'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:24:13 + | +LL | assert!(('a'..='z').contains(&'a')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a'.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:25:13 + | +LL | assert!(('A'..='Z').contains(&'A')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'A'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:37:13 | LL | assert!(matches!(b'1', b'0'..=b'9')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:31:13 + --> $DIR/manual_is_ascii_check.rs:38:13 | LL | assert!(matches!('X', 'A'..='Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:32:13 + --> $DIR/manual_is_ascii_check.rs:39:13 | LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:42:23 + --> $DIR/manual_is_ascii_check.rs:49:23 | LL | const FOO: bool = matches!('x', '0'..='9'); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` -error: aborting due to 12 previous errors +error: aborting due to 17 previous errors From 55fdd1e78c73b67adce581fbf09aa0a1b6f07c61 Mon Sep 17 00:00:00 2001 From: naosense Date: Tue, 13 Dec 2022 10:50:49 +0800 Subject: [PATCH 337/524] replace reference with value --- clippy_lints/src/manual_is_ascii_check.rs | 8 ++++++-- tests/ui/manual_is_ascii_check.fixed | 12 ++++++------ tests/ui/manual_is_ascii_check.stderr | 12 ++++++------ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index eaaaf0c65812..b1578627b50b 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -3,7 +3,7 @@ use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros: use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; +use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{def_id::DefId, sym, Span}; @@ -86,8 +86,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { && path.ident.name == sym!(contains) && let Some(higher::Range { start: Some(start), end: Some(end), limits: RangeLimits::Closed }) = higher::Range::hir(receiver) { - let range = check_range(start, end); + let range = check_range(start, end); + if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind { + check_is_ascii(cx, expr.span, e, &range); + } else { check_is_ascii(cx, expr.span, arg, &range); + } } } diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index b2f45aba59aa..b5e8b9c19cbe 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -16,13 +16,13 @@ fn main() { assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); - assert!(&b'0'.is_ascii_digit()); - assert!(&b'a'.is_ascii_lowercase()); - assert!(&b'A'.is_ascii_uppercase()); + assert!(b'0'.is_ascii_digit()); + assert!(b'a'.is_ascii_lowercase()); + assert!(b'A'.is_ascii_uppercase()); - assert!(&'0'.is_ascii_digit()); - assert!(&'a'.is_ascii_lowercase()); - assert!(&'A'.is_ascii_uppercase()); + assert!('0'.is_ascii_digit()); + assert!('a'.is_ascii_lowercase()); + assert!('A'.is_ascii_uppercase()); } #[clippy::msrv = "1.23"] diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index 797952a3aba5..ae747d33b585 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -46,37 +46,37 @@ error: manual check for common ascii range --> $DIR/manual_is_ascii_check.rs:19:13 | LL | assert!((b'0'..=b'9').contains(&b'0')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&b'0'.is_ascii_digit()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'0'.is_ascii_digit()` error: manual check for common ascii range --> $DIR/manual_is_ascii_check.rs:20:13 | LL | assert!((b'a'..=b'z').contains(&b'a')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&b'a'.is_ascii_lowercase()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'a'.is_ascii_lowercase()` error: manual check for common ascii range --> $DIR/manual_is_ascii_check.rs:21:13 | LL | assert!((b'A'..=b'Z').contains(&b'A')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&b'A'.is_ascii_uppercase()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'A'.is_ascii_uppercase()` error: manual check for common ascii range --> $DIR/manual_is_ascii_check.rs:23:13 | LL | assert!(('0'..='9').contains(&'0')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'0'.is_ascii_digit()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'0'.is_ascii_digit()` error: manual check for common ascii range --> $DIR/manual_is_ascii_check.rs:24:13 | LL | assert!(('a'..='z').contains(&'a')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a'.is_ascii_lowercase()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'a'.is_ascii_lowercase()` error: manual check for common ascii range --> $DIR/manual_is_ascii_check.rs:25:13 | LL | assert!(('A'..='Z').contains(&'A')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'A'.is_ascii_uppercase()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'A'.is_ascii_uppercase()` error: manual check for common ascii range --> $DIR/manual_is_ascii_check.rs:37:13 From 949d0709bd814c1191ab8c0e6c689db3f7ff07e4 Mon Sep 17 00:00:00 2001 From: naosense Date: Tue, 13 Dec 2022 11:11:52 +0800 Subject: [PATCH 338/524] improve document --- clippy_lints/src/manual_is_ascii_check.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index b1578627b50b..6ee94551d70e 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -24,6 +24,14 @@ declare_clippy_lint! { /// assert!(matches!(b'X', b'A'..=b'Z')); /// assert!(matches!('2', '0'..='9')); /// assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + /// + /// assert!((b'0'..=b'9').contains(&b'0')); + /// assert!((b'a'..=b'z').contains(&b'a')); + /// assert!((b'A'..=b'Z').contains(&b'A')); + /// + /// assert!(('0'..='9').contains(&'0')); + /// assert!(('a'..='z').contains(&'a')); + /// assert!(('A'..='Z').contains(&'A')); /// } /// ``` /// Use instead: @@ -33,6 +41,14 @@ declare_clippy_lint! { /// assert!(b'X'.is_ascii_uppercase()); /// assert!('2'.is_ascii_digit()); /// assert!('x'.is_ascii_alphabetic()); + /// + /// assert!(b'0'.is_ascii_digit()); + /// assert!(b'a'.is_ascii_lowercase()); + /// assert!(b'A'.is_ascii_uppercase()); + /// + /// assert!('0'.is_ascii_digit()); + /// assert!('a'.is_ascii_lowercase()); + /// assert!('A'.is_ascii_uppercase()); /// } /// ``` #[clippy::version = "1.66.0"] From 1f862c2ad31a64beb86a2c6b0c1d2a1947b8173b Mon Sep 17 00:00:00 2001 From: naosense Date: Tue, 13 Dec 2022 16:50:09 +0800 Subject: [PATCH 339/524] remove assert macro --- clippy_lints/src/manual_is_ascii_check.rs | 20 +++---- tests/ui/manual_is_ascii_check.fixed | 19 ++++--- tests/ui/manual_is_ascii_check.rs | 19 ++++--- tests/ui/manual_is_ascii_check.stderr | 64 +++++++++++++++-------- 4 files changed, 71 insertions(+), 51 deletions(-) diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 6ee94551d70e..d9ef7dffa020 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -25,13 +25,9 @@ declare_clippy_lint! { /// assert!(matches!('2', '0'..='9')); /// assert!(matches!('x', 'A'..='Z' | 'a'..='z')); /// - /// assert!((b'0'..=b'9').contains(&b'0')); - /// assert!((b'a'..=b'z').contains(&b'a')); - /// assert!((b'A'..=b'Z').contains(&b'A')); - /// - /// assert!(('0'..='9').contains(&'0')); - /// assert!(('a'..='z').contains(&'a')); - /// assert!(('A'..='Z').contains(&'A')); + /// ('0'..='9').contains(&'0'); + /// ('a'..='z').contains(&'a'); + /// ('A'..='Z').contains(&'A'); /// } /// ``` /// Use instead: @@ -42,13 +38,9 @@ declare_clippy_lint! { /// assert!('2'.is_ascii_digit()); /// assert!('x'.is_ascii_alphabetic()); /// - /// assert!(b'0'.is_ascii_digit()); - /// assert!(b'a'.is_ascii_lowercase()); - /// assert!(b'A'.is_ascii_uppercase()); - /// - /// assert!('0'.is_ascii_digit()); - /// assert!('a'.is_ascii_lowercase()); - /// assert!('A'.is_ascii_uppercase()); + /// '0'.is_ascii_digit(); + /// 'a'.is_ascii_lowercase(); + /// 'A'.is_ascii_uppercase(); /// } /// ``` #[clippy::version = "1.66.0"] diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index b5e8b9c19cbe..5b2b44c2fdb2 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -16,13 +16,18 @@ fn main() { assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); - assert!(b'0'.is_ascii_digit()); - assert!(b'a'.is_ascii_lowercase()); - assert!(b'A'.is_ascii_uppercase()); - - assert!('0'.is_ascii_digit()); - assert!('a'.is_ascii_lowercase()); - assert!('A'.is_ascii_uppercase()); + b'0'.is_ascii_digit(); + b'a'.is_ascii_lowercase(); + b'A'.is_ascii_uppercase(); + + '0'.is_ascii_digit(); + 'a'.is_ascii_lowercase(); + 'A'.is_ascii_uppercase(); + + let cool_letter = &'g'; + cool_letter.is_ascii_digit(); + cool_letter.is_ascii_lowercase(); + cool_letter.is_ascii_uppercase(); } #[clippy::msrv = "1.23"] diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index 7f1ee88fc743..c9433f33a1b6 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -16,13 +16,18 @@ fn main() { assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); - assert!((b'0'..=b'9').contains(&b'0')); - assert!((b'a'..=b'z').contains(&b'a')); - assert!((b'A'..=b'Z').contains(&b'A')); - - assert!(('0'..='9').contains(&'0')); - assert!(('a'..='z').contains(&'a')); - assert!(('A'..='Z').contains(&'A')); + (b'0'..=b'9').contains(&b'0'); + (b'a'..=b'z').contains(&b'a'); + (b'A'..=b'Z').contains(&b'A'); + + ('0'..='9').contains(&'0'); + ('a'..='z').contains(&'a'); + ('A'..='Z').contains(&'A'); + + let cool_letter = &'g'; + ('0'..='9').contains(cool_letter); + ('a'..='z').contains(cool_letter); + ('A'..='Z').contains(cool_letter); } #[clippy::msrv = "1.23"] diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index ae747d33b585..ee60188506d6 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -43,64 +43,82 @@ LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:19:13 + --> $DIR/manual_is_ascii_check.rs:19:5 | -LL | assert!((b'0'..=b'9').contains(&b'0')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'0'.is_ascii_digit()` +LL | (b'0'..=b'9').contains(&b'0'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'0'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:20:13 + --> $DIR/manual_is_ascii_check.rs:20:5 | -LL | assert!((b'a'..=b'z').contains(&b'a')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'a'.is_ascii_lowercase()` +LL | (b'a'..=b'z').contains(&b'a'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'a'.is_ascii_lowercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:21:13 + --> $DIR/manual_is_ascii_check.rs:21:5 | -LL | assert!((b'A'..=b'Z').contains(&b'A')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'A'.is_ascii_uppercase()` +LL | (b'A'..=b'Z').contains(&b'A'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'A'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:23:13 + --> $DIR/manual_is_ascii_check.rs:23:5 | -LL | assert!(('0'..='9').contains(&'0')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'0'.is_ascii_digit()` +LL | ('0'..='9').contains(&'0'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'0'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:24:13 + --> $DIR/manual_is_ascii_check.rs:24:5 | -LL | assert!(('a'..='z').contains(&'a')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'a'.is_ascii_lowercase()` +LL | ('a'..='z').contains(&'a'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'a'.is_ascii_lowercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:25:13 + --> $DIR/manual_is_ascii_check.rs:25:5 | -LL | assert!(('A'..='Z').contains(&'A')); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'A'.is_ascii_uppercase()` +LL | ('A'..='Z').contains(&'A'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'A'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:37:13 + --> $DIR/manual_is_ascii_check.rs:28:5 + | +LL | ('0'..='9').contains(cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:29:5 + | +LL | ('a'..='z').contains(cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:30:5 + | +LL | ('A'..='Z').contains(cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:42:13 | LL | assert!(matches!(b'1', b'0'..=b'9')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:38:13 + --> $DIR/manual_is_ascii_check.rs:43:13 | LL | assert!(matches!('X', 'A'..='Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:39:13 + --> $DIR/manual_is_ascii_check.rs:44:13 | LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:49:23 + --> $DIR/manual_is_ascii_check.rs:54:23 | LL | const FOO: bool = matches!('x', '0'..='9'); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` -error: aborting due to 17 previous errors +error: aborting due to 20 previous errors From ad55e4c9721f3283205f1518facaae5e66380e45 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 26 Nov 2022 21:09:39 +0000 Subject: [PATCH 340/524] Use ty::OpaqueTy everywhere --- clippy_lints/src/future_not_send.rs | 8 ++++---- clippy_utils/src/ty.rs | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 61934a914263..8a7a65c86001 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -4,7 +4,7 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Clause, EarlyBinder, Opaque, PredicateKind}; +use rustc_middle::ty::{Clause, EarlyBinder, Opaque, OpaqueTy, PredicateKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; @@ -62,11 +62,11 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { return; } let ret_ty = return_ty(cx, hir_id); - if let Opaque(id, subst) = *ret_ty.kind() { - let preds = cx.tcx.explicit_item_bounds(id); + if let Opaque(OpaqueTy { def_id, substs }) = *ret_ty.kind() { + let preds = cx.tcx.explicit_item_bounds(def_id); let mut is_future = false; for &(p, _span) in preds { - let p = EarlyBinder(p).subst(cx.tcx, subst); + let p = EarlyBinder(p).subst(cx.tcx, substs); if let Some(trait_pred) = p.to_opt_poly_trait_pred() { if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() { is_future = true; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index bfb2d472a393..f5f70b195c98 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -79,7 +79,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return true; } - if let ty::Opaque(def_id, _) = *inner_ty.kind() { + if let ty::Opaque(ty::OpaqueTy { def_id, substs: _ }) = *inner_ty.kind() { for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through @@ -250,7 +250,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { is_must_use_ty(cx, *ty) }, ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)), - ty::Opaque(def_id, _) => { + ty::Opaque(ty::OpaqueTy { def_id, substs: _ }) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { @@ -631,7 +631,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))), - ty::Opaque(id, _) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(id), cx.tcx.opt_parent(id)), + ty::Opaque(ty::OpaqueTy{ def_id, substs: _ }) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), ty::Dynamic(bounds, _, _) => { let lang_items = cx.tcx.lang_items(); From a274e7e9a2d6373d1989d187cdf8475e55be9f6f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 26 Nov 2022 21:21:20 +0000 Subject: [PATCH 341/524] ProjectionTy.item_def_id -> ProjectionTy.def_id --- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/methods/needless_collect.rs | 2 +- clippy_utils/src/ty.rs | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 38329659e02b..ad5a1b2beb70 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1330,7 +1330,7 @@ fn replace_types<'tcx>( && let Some(term_ty) = projection_predicate.term.ty() && let ty::Param(term_param_ty) = term_ty.kind() { - let item_def_id = projection_predicate.projection_ty.item_def_id; + let item_def_id = projection_predicate.projection_ty.def_id; let assoc_item = cx.tcx.associated_item(item_def_id); let projection = cx.tcx .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, [])); diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 4c133c06a157..982f99c27163 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -493,7 +493,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { .filter_by_name_unhygienic(is_empty) .any(|item| is_is_empty(cx, item)) }), - ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), + ty::Projection(ref proj) => has_is_empty_impl(cx, proj.def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did()), ty::Array(..) | ty::Slice(..) | ty::Str => true, _ => false, diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index b088e642e0e9..f4d3ef3b7425 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -151,7 +151,7 @@ fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty]) && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( cx.param_env, - cx.tcx.mk_projection(into_iter_item_proj.item_def_id, into_iter_item_proj.substs) + cx.tcx.mk_projection(into_iter_item_proj.def_id, into_iter_item_proj.substs) ) { iter_item_ty == into_iter_item_ty diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index f5f70b195c98..11e41d1958ce 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -685,7 +685,7 @@ fn sig_from_bounds<'tcx>( inputs = Some(i); }, PredicateKind::Clause(ty::Clause::Projection(p)) - if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() + if Some(p.projection_ty.def_id) == lang_items.fn_once_output() && p.projection_ty.self_ty() == ty => { if output.is_some() { @@ -708,7 +708,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O for (pred, _) in cx .tcx - .bound_explicit_item_bounds(ty.item_def_id) + .bound_explicit_item_bounds(ty.def_id) .subst_iter_copied(cx.tcx, ty.substs) { match pred.kind().skip_binder() { @@ -726,7 +726,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O inputs = Some(i); }, PredicateKind::Clause(ty::Clause::Projection(p)) - if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => + if Some(p.projection_ty.def_id) == lang_items.fn_once_output() => { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? @@ -1041,7 +1041,7 @@ pub fn make_projection<'tcx>( Some(ProjectionTy { substs, - item_def_id: assoc_item.def_id, + def_id: assoc_item.def_id, }) } helper( @@ -1081,7 +1081,7 @@ pub fn make_normalized_projection<'tcx>( ); return None; } - match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.item_def_id, ty.substs)) { + match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.def_id, ty.substs)) { Ok(ty) => Some(ty), Err(e) => { debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); From 89b884054334c2f0334c4a6218fcb489813a6633 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 26 Nov 2022 21:32:01 +0000 Subject: [PATCH 342/524] squash OpaqueTy and ProjectionTy into AliasTy --- clippy_lints/src/future_not_send.rs | 4 ++-- clippy_utils/src/ty.rs | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 8a7a65c86001..3ff774867b1e 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -4,7 +4,7 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Clause, EarlyBinder, Opaque, OpaqueTy, PredicateKind}; +use rustc_middle::ty::{AliasTy, Clause, EarlyBinder, Opaque, PredicateKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { return; } let ret_ty = return_ty(cx, hir_id); - if let Opaque(OpaqueTy { def_id, substs }) = *ret_ty.kind() { + if let Opaque(AliasTy { def_id, substs }) = *ret_ty.kind() { let preds = cx.tcx.explicit_item_bounds(def_id); let mut is_future = false; for &(p, _span) in preds { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 11e41d1958ce..bddab7eca53b 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -17,7 +17,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind, - ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + AliasTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; @@ -79,7 +79,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return true; } - if let ty::Opaque(ty::OpaqueTy { def_id, substs: _ }) = *inner_ty.kind() { + if let ty::Opaque(ty::AliasTy { def_id, substs: _ }) = *inner_ty.kind() { for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through @@ -250,7 +250,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { is_must_use_ty(cx, *ty) }, ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)), - ty::Opaque(ty::OpaqueTy { def_id, substs: _ }) => { + ty::Opaque(ty::AliasTy { def_id, substs: _ }) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { @@ -631,7 +631,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))), - ty::Opaque(ty::OpaqueTy{ def_id, substs: _ }) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)), + ty::Opaque(ty::AliasTy { def_id, substs: _ }) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), ty::Dynamic(bounds, _, _) => { let lang_items = cx.tcx.lang_items(); @@ -701,7 +701,7 @@ fn sig_from_bounds<'tcx>( inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id)) } -fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option> { +fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option> { let mut inputs = None; let mut output = None; let lang_items = cx.tcx.lang_items(); @@ -980,13 +980,13 @@ pub fn make_projection<'tcx>( container_id: DefId, assoc_ty: Symbol, substs: impl IntoIterator>>, -) -> Option> { +) -> Option> { fn helper<'tcx>( tcx: TyCtxt<'tcx>, container_id: DefId, assoc_ty: Symbol, substs: SubstsRef<'tcx>, - ) -> Option> { + ) -> Option> { let Some(assoc_item) = tcx .associated_items(container_id) .find_by_name_and_kind(tcx, Ident::with_dummy_span(assoc_ty), AssocKind::Type, container_id) @@ -1039,7 +1039,7 @@ pub fn make_projection<'tcx>( } } - Some(ProjectionTy { + Some(AliasTy { substs, def_id: assoc_item.def_id, }) @@ -1065,7 +1065,7 @@ pub fn make_normalized_projection<'tcx>( assoc_ty: Symbol, substs: impl IntoIterator>>, ) -> Option> { - fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: ProjectionTy<'tcx>) -> Option> { + fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { #[cfg(debug_assertions)] if let Some((i, subst)) = ty .substs From 957ab6ae52706e3428ca56e727eed9d3333d8170 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 26 Nov 2022 21:51:55 +0000 Subject: [PATCH 343/524] Combine projection and opaque into alias --- clippy_lints/src/dereference.rs | 8 ++++---- clippy_lints/src/future_not_send.rs | 4 ++-- clippy_lints/src/len_zero.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 2 +- clippy_utils/src/ty.rs | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index ad5a1b2beb70..31183266acfc 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1244,7 +1244,7 @@ fn is_mixed_projection_predicate<'tcx>( let mut projection_ty = projection_predicate.projection_ty; loop { match projection_ty.self_ty().kind() { - ty::Projection(inner_projection_ty) => { + ty::Alias(ty::Projection, inner_projection_ty) => { projection_ty = *inner_projection_ty; } ty::Param(param_ty) => { @@ -1390,8 +1390,8 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc continue; }, ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), - ty::Projection(_) if ty.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty), - ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => { + ty::Alias(ty::Projection, _) if ty.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty), + ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Alias(ty::Opaque, ..) | ty::Placeholder(_) | ty::Dynamic(..) => { Position::ReborrowStable(precedence).into() }, ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => { @@ -1417,7 +1417,7 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc | ty::Closure(..) | ty::Never | ty::Tuple(_) - | ty::Projection(_) => { + | ty::Alias(ty::Projection, _) => { Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds())).into() }, }; diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 3ff774867b1e..fcdac90fc237 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -4,7 +4,7 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{AliasTy, Clause, EarlyBinder, Opaque, PredicateKind}; +use rustc_middle::ty::{self, AliasTy, Clause, EarlyBinder, PredicateKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { return; } let ret_ty = return_ty(cx, hir_id); - if let Opaque(AliasTy { def_id, substs }) = *ret_ty.kind() { + if let ty::Alias(ty::Opaque, AliasTy { def_id, substs }) = *ret_ty.kind() { let preds = cx.tcx.explicit_item_bounds(def_id); let mut is_future = false; for &(p, _span) in preds { diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 982f99c27163..73841f9aa9a2 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -493,7 +493,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { .filter_by_name_unhygienic(is_empty) .any(|item| is_is_empty(cx, item)) }), - ty::Projection(ref proj) => has_is_empty_impl(cx, proj.def_id), + ty::Alias(ty::Projection, ref proj) => has_is_empty_impl(cx, proj.def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did()), ty::Array(..) | ty::Slice(..) | ty::Str => true, _ => false, diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index e053a9dc8881..8bf542ada04d 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -82,7 +82,7 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { ty::Ref(_, _, hir::Mutability::Mut) => { return Err((span, "mutable references in const fn are unstable".into())); }, - ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), + ty::Alias(ty::Opaque, ..) => return Err((span, "`impl Trait` in const fn is unstable".into())), ty::FnPtr(..) => { return Err((span, "function pointers in const fn are unstable".into())); }, diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index bddab7eca53b..33f3b3af3dc0 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -79,7 +79,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return true; } - if let ty::Opaque(ty::AliasTy { def_id, substs: _ }) = *inner_ty.kind() { + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs: _ }) = *inner_ty.kind() { for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through @@ -250,7 +250,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { is_must_use_ty(cx, *ty) }, ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)), - ty::Opaque(ty::AliasTy { def_id, substs: _ }) => { + ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs: _ }) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { @@ -631,7 +631,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))), - ty::Opaque(ty::AliasTy { def_id, substs: _ }) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)), + ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs: _ }) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), ty::Dynamic(bounds, _, _) => { let lang_items = cx.tcx.lang_items(); @@ -650,7 +650,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option None, } }, - ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) { + ty::Alias(ty::Projection, proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) { Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty), _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)), }, From 2532c56d8643c9ee343da8dff60828fcfe77d065 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 13 Dec 2022 18:29:22 +0100 Subject: [PATCH 344/524] Update version attribute for 1.66 lints --- clippy_lints/src/box_default.rs | 2 +- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/disallowed_macros.rs | 2 +- clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/implicit_saturating_add.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 36daceabe0be..91900542af83 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// ```rust /// let x: Box = Box::default(); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub BOX_DEFAULT, perf, "Using Box::new(T::default()) instead of Box::default()" diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index c6d505c4a181..161e3a698e9e 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -641,7 +641,7 @@ declare_clippy_lint! { /// ```rust,ignore /// let _: = 0_u64; /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.66.0"] pub CAST_NAN_TO_INT, suspicious, "casting a known floating-point NaN into an integer" diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 68122b4cef57..1971cab64ef3 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -47,7 +47,7 @@ declare_clippy_lint! { /// value: usize, /// } /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub DISALLOWED_MACROS, style, "use of a disallowed macro" diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index f0995a81329d..08976bde8829 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -122,7 +122,7 @@ declare_clippy_lint! { /// /// If a format string contains a numbered argument that cannot be inlined /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub UNINLINED_FORMAT_ARGS, style, "using non-inlined variables in `format!` calls" diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index bf1351829c6a..6e19343931ec 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -31,7 +31,7 @@ declare_clippy_lint! { /// /// u = u.saturating_add(1); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub IMPLICIT_SATURATING_ADD, style, "Perform saturating addition instead of implicitly checking max bound of data type" diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d2913680cbb7..30883ba8d051 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3059,7 +3059,7 @@ declare_clippy_lint! { /// let map: HashMap = HashMap::new(); /// let values = map.values().collect::>(); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub ITER_KV_MAP, complexity, "iterating on map using `iter` when `keys` or `values` would do" From 2855a0f117c86d89b5ee4ec5eba50cd1700c6e08 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 13 Dec 2022 18:30:49 +0100 Subject: [PATCH 345/524] Changelog for Rust 1.66 :santa: --- CHANGELOG.md | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23912bb3ed6b..cc3f785bb37e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,181 @@ document. ## Unreleased / Beta / In Rust Nightly -[b52fb523...master](https://github.com/rust-lang/rust-clippy/compare/b52fb523...master) +[4f142aa1...master](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...master) -## Rust 1.65 +## Rust 1.66 Current stable, released 2022-11-03 +[b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1) + +### New Lints + +* [`manual_clamp`] + [#9484](https://github.com/rust-lang/rust-clippy/pull/9484) +* [`missing_trait_methods`] + [#9670](https://github.com/rust-lang/rust-clippy/pull/9670) +* [`unused_format_specs`] + [#9637](https://github.com/rust-lang/rust-clippy/pull/9637) +* [`iter_kv_map`] + [#9409](https://github.com/rust-lang/rust-clippy/pull/9409) +* [`manual_filter`] + [#9451](https://github.com/rust-lang/rust-clippy/pull/9451) +* [`box_default`] + [#9511](https://github.com/rust-lang/rust-clippy/pull/9511) +* [`implicit_saturating_add`] + [#9549](https://github.com/rust-lang/rust-clippy/pull/9549) +* [`as_ptr_cast_mut`] + [#9572](https://github.com/rust-lang/rust-clippy/pull/9572) +* [`disallowed_macros`] + [#9495](https://github.com/rust-lang/rust-clippy/pull/9495) +* [`partial_pub_fields`] + [#9658](https://github.com/rust-lang/rust-clippy/pull/9658) +* [`uninlined_format_args`] + [#9233](https://github.com/rust-lang/rust-clippy/pull/9233) +* [`cast_nan_to_int`] + [#9617](https://github.com/rust-lang/rust-clippy/pull/9617) + +### Moves and Deprecations + +* `positional_named_format_parameters` was uplifted to rustc under the new name + `named_arguments_used_positionally` + [#8518](https://github.com/rust-lang/rust-clippy/pull/8518) +* Moved [`implicit_saturating_sub`] to `style` (Now warn-by-default) + [#9584](https://github.com/rust-lang/rust-clippy/pull/9584) +* Moved `derive_partial_eq_without_eq` to `nursery` (now allow-by-default) + [#9536](https://github.com/rust-lang/rust-clippy/pull/9536) + +### Enhancements + +* [`nonstandard_macro_braces`]: Now includes `matches!()` in the default lint config + [#9471](https://github.com/rust-lang/rust-clippy/pull/9471) +* [`suboptimal_flops`]: Now supports multiplication and subtraction operations + [#9581](https://github.com/rust-lang/rust-clippy/pull/9581) +* [`arithmetic_side_effects`]: Now detects cases with literals behind references + [#9587](https://github.com/rust-lang/rust-clippy/pull/9587) +* [`upper_case_acronyms`]: Now also checks enum names + [#9580](https://github.com/rust-lang/rust-clippy/pull/9580) +* [`needless_borrowed_reference`]: Now lints nested patterns + [#9573](https://github.com/rust-lang/rust-clippy/pull/9573) +* [`unnecessary_cast`]: Now works for non-trivial non-literal expressions + [#9576](https://github.com/rust-lang/rust-clippy/pull/9576) +* [`arithmetic_side_effects`]: Now detects operations with custom types + [#9559](https://github.com/rust-lang/rust-clippy/pull/9559) +* [`disallowed_methods`], [`disallowed_types`]: Not correctly lints types, functions and macros + with the same path + [#9495](https://github.com/rust-lang/rust-clippy/pull/9495) +* [`self_named_module_files`], [`mod_module_files`]: Now take remapped path prefixes into account + [#9475](https://github.com/rust-lang/rust-clippy/pull/9475) +* [`bool_to_int_with_if`]: Now detects the inverse if case + [#9476](https://github.com/rust-lang/rust-clippy/pull/9476) + +### False Positive Fixes + +* [`arithmetic_side_effects`]: Now allows operations that can't overflow + [#9474](https://github.com/rust-lang/rust-clippy/pull/9474) +* [`unnecessary_lazy_evaluations`]: No longer lints in external macros + [#9486](https://github.com/rust-lang/rust-clippy/pull/9486) +* [`needless_borrow`], [`explicit_auto_deref`]: No longer lint on unions that require the reference + [#9490](https://github.com/rust-lang/rust-clippy/pull/9490) +* [`almost_complete_letter_range`]: No longer lints in external macros + [#9467](https://github.com/rust-lang/rust-clippy/pull/9467) +* [`drop_copy`]: No longer lints on idiomatic cases in match arms + [#9491](https://github.com/rust-lang/rust-clippy/pull/9491) +* [`question_mark`]: No longer lints in const context + [#9487](https://github.com/rust-lang/rust-clippy/pull/9487) +* [`collapsible_if`]: Suggestion now work in macros + [#9410](https://github.com/rust-lang/rust-clippy/pull/9410) +* [`std_instead_of_core`]: No longer triggers on unstable modules + [#9545](https://github.com/rust-lang/rust-clippy/pull/9545) +* [`unused_peekable`]: No longer lints, if the peak is done in a closure or function + [#9465](https://github.com/rust-lang/rust-clippy/pull/9465) +* [`useless_attribute`]: No longer lints on `#[allow]` attributes for [`unsafe_removed_from_name`] + [#9593](https://github.com/rust-lang/rust-clippy/pull/9593) +* [`unnecessary_lazy_evaluations`]: No longer suggest switching to early evaluation when type has + custom `Drop` implementation + [#9551](https://github.com/rust-lang/rust-clippy/pull/9551) +* [`unnecessary_cast`]: No longer lints on negative hexadecimal literals when cast as floats + [#9609](https://github.com/rust-lang/rust-clippy/pull/9609) +* [`use_self`]: No longer lints in proc macros + [#9454](https://github.com/rust-lang/rust-clippy/pull/9454) +* [`never_loop`]: Now takes `let ... else` statements into consideration. + [#9496](https://github.com/rust-lang/rust-clippy/pull/9496) +* [`default_numeric_fallback`]: Now ignores constants + [#9636](https://github.com/rust-lang/rust-clippy/pull/9636) +* [`uninit_vec`]: No longer lints `Vec::set_len(0)` + [#9519](https://github.com/rust-lang/rust-clippy/pull/9519) +* [`arithmetic_side_effects`]: Now ignores references + [9507](https://github.com/rust-lang/rust-clippy/pull/9507) +* [`large_stack_arrays`]: No longer lints inside static items + [#9466](https://github.com/rust-lang/rust-clippy/pull/9466) +* [`ref_option_ref`]: No longer lints if the inner reference is mutable + [#9684](https://github.com/rust-lang/rust-clippy/pull/9684) +* [`ptr_arg`]: No longer lints if the argument is used as an incomplete trait object + [#9645](https://github.com/rust-lang/rust-clippy/pull/9645) +* [`should_implement_trait`]: Now also works for `default` methods + [#9546](https://github.com/rust-lang/rust-clippy/pull/9546) + +### Suggestion Fixes/Improvements + +* [`derivable_impls`]: The suggestion is now machine applicable + [#9429](https://github.com/rust-lang/rust-clippy/pull/9429) +* [`match_single_binding`]: The suggestion now handles scrutinies with side effects better + [#9601](https://github.com/rust-lang/rust-clippy/pull/9601) +* [`zero_prefixed_literal`]: Only suggests using octal numbers, if this is possible + [#9652](https://github.com/rust-lang/rust-clippy/pull/9652) +* [`rc_buffer`]: The suggestion is no longer machine applicable to avoid semantic changes + [#9633](https://github.com/rust-lang/rust-clippy/pull/9633) +* [`print_literal`], [`write_literal`], [`uninlined_format_args`]: The suggestion now ignores + comments after the macro call. + [#9586](https://github.com/rust-lang/rust-clippy/pull/9586) +* [`expect_fun_call`]:Improved the suggestion for `format!` calls with captured variables + [#9586](https://github.com/rust-lang/rust-clippy/pull/9586) +* [`nonstandard_macro_braces`]: The suggestion is now machine applicable and will no longer + replace brackets inside the macro argument. + [#9499](https://github.com/rust-lang/rust-clippy/pull/9499) +* [`from_over_into`]: The suggestion is now a machine applicable and contains explanations + [#9649](https://github.com/rust-lang/rust-clippy/pull/9649) +* [`needless_return`]: The automatic suggestion now removes all required semicolons + [#9497](https://github.com/rust-lang/rust-clippy/pull/9497) +* [`to_string_in_format_args`]: The suggestion now keeps parenthesis around values + [#9590](https://github.com/rust-lang/rust-clippy/pull/9590) +* [`manual_assert`]: The suggestion now preserves comments + [#9479](https://github.com/rust-lang/rust-clippy/pull/9479) +* [`redundant_allocation`]: The suggestion applicability is now marked `MaybeIncorrect` to + avoid semantic changes + [#9634](https://github.com/rust-lang/rust-clippy/pull/9634) +* [`assertions_on_result_states`]: The suggestion has been corrected, for cases where the + `assert!` is not in a statement. + [#9453](https://github.com/rust-lang/rust-clippy/pull/9453) +* [`nonminimal_bool`]: The suggestion no longer expands macros + [#9457](https://github.com/rust-lang/rust-clippy/pull/9457) +* [`collapsible_match`]: Now specifies field names, when a struct is destructed + [#9685](https://github.com/rust-lang/rust-clippy/pull/9685) +* [`unnecessary_cast`]: The suggestion now adds parenthesis for negative numbers + [#9577](https://github.com/rust-lang/rust-clippy/pull/9577) +* [`redundant_closure`]: The suggestion now works for `impl FnMut` arguments + [#9556](https://github.com/rust-lang/rust-clippy/pull/9556) + +### ICE Fixes + +* [`unnecessary_to_owned`]: Avoid ICEs in favor of false negatives if information is missing + [#9505](https://github.com/rust-lang/rust-clippy/pull/9505) +* [`manual_range_contains`]: No longer ICEs on values behind references + [#9627](https://github.com/rust-lang/rust-clippy/pull/9627) +* [`needless_pass_by_value`]: No longer ICEs on unsized `dyn Fn` arguments + [#9531](https://github.com/rust-lang/rust-clippy/pull/9531) +* `*_interior_mutable_const` lints: no longer ICE on const unions containing `!Freeze` types + +### Others + +* Released `rustc_tools_util` for version information on `Crates.io`. (Further adjustments will + not be published as part of this changelog) + +## Rust 1.65 + +Released 2022-11-03 + [3c7e7dbc...b52fb523](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...b52fb523) ### Important Changes From 65069d5c5b6db1bdf74e4f9243db7ef82d6a3776 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 13 Dec 2022 11:07:42 +0000 Subject: [PATCH 346/524] Ensure no one constructs `AliasTy`s themselves --- clippy_lints/src/future_not_send.rs | 2 +- clippy_utils/src/ty.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index fcdac90fc237..989f83cf80d5 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { return; } let ret_ty = return_ty(cx, hir_id); - if let ty::Alias(ty::Opaque, AliasTy { def_id, substs }) = *ret_ty.kind() { + if let ty::Alias(ty::Opaque, AliasTy { def_id, substs, .. }) = *ret_ty.kind() { let preds = cx.tcx.explicit_item_bounds(def_id); let mut is_future = false; for &(p, _span) in preds { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 33f3b3af3dc0..a6bcb134d408 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -79,7 +79,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return true; } - if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs: _ }) = *inner_ty.kind() { + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *inner_ty.kind() { for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through @@ -250,7 +250,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { is_must_use_ty(cx, *ty) }, ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs: _ }) => { + ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { @@ -631,7 +631,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs: _ }) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)), + ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), ty::Dynamic(bounds, _, _) => { let lang_items = cx.tcx.lang_items(); @@ -1039,10 +1039,10 @@ pub fn make_projection<'tcx>( } } - Some(AliasTy { + Some(tcx.mk_alias_ty( + assoc_item.def_id, substs, - def_id: assoc_item.def_id, - }) + )) } helper( tcx, From fa87abf963d2fe8c1c24ef68ee764f9faeda0d47 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 13 Dec 2022 11:25:31 +0000 Subject: [PATCH 347/524] Remove TraitRef::new --- clippy_lints/src/derive.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 9e596ca8157e..3f0b165f2b60 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -15,7 +15,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, - TraitRef, Ty, TyCtxt, + Ty, TyCtxt, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -513,9 +513,9 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain( params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Trait(TraitPredicate { - trait_ref: TraitRef::new( + trait_ref: tcx.mk_trait_ref( eq_trait_id, - tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))), + [tcx.mk_param_from_def(param)], ), constness: BoundConstness::NotConst, polarity: ImplPolarity::Positive, From 71019aa07613724f1421edc4ead49b04728a7959 Mon Sep 17 00:00:00 2001 From: Fridtjof Stoldt Date: Wed, 14 Dec 2022 08:22:17 +0100 Subject: [PATCH 348/524] Address review comments <3 Co-authored-by: Takayuki Nakata Co-authored-by: Alex Macleod --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc3f785bb37e..f0a9f8c77a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ document. ## Rust 1.66 -Current stable, released 2022-11-03 +Current stable, released 2022-12-15 [b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1) @@ -110,8 +110,8 @@ Current stable, released 2022-11-03 [#9636](https://github.com/rust-lang/rust-clippy/pull/9636) * [`uninit_vec`]: No longer lints `Vec::set_len(0)` [#9519](https://github.com/rust-lang/rust-clippy/pull/9519) -* [`arithmetic_side_effects`]: Now ignores references - [9507](https://github.com/rust-lang/rust-clippy/pull/9507) +* [`arithmetic_side_effects`]: Now ignores references to integer types + [#9507](https://github.com/rust-lang/rust-clippy/pull/9507) * [`large_stack_arrays`]: No longer lints inside static items [#9466](https://github.com/rust-lang/rust-clippy/pull/9466) * [`ref_option_ref`]: No longer lints if the inner reference is mutable @@ -171,6 +171,7 @@ Current stable, released 2022-11-03 * [`needless_pass_by_value`]: No longer ICEs on unsized `dyn Fn` arguments [#9531](https://github.com/rust-lang/rust-clippy/pull/9531) * `*_interior_mutable_const` lints: no longer ICE on const unions containing `!Freeze` types + [#9539](https://github.com/rust-lang/rust-clippy/pull/9539) ### Others From e723fc4f564efa916beeaa59de3c55ccd9006a64 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 15 Dec 2022 15:13:19 +1100 Subject: [PATCH 349/524] Merge `SimplifiedTypeGen` into `SimplifiedType`. `SimplifiedTypeGen` is the only instantiation used, so we don't need the generic parameter. --- clippy_utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 90192f46cbfa..652f8b4d3c56 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -97,7 +97,7 @@ use rustc_middle::hir::place::PlaceBase; use rustc_middle::ty as rustc_ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{ +use rustc_middle::ty::fast_reject::SimplifiedType::{ ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType, PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType, }; From 004b885c0afe34d6f8e6649f1faa8bbef656172c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 15 Dec 2022 12:42:08 +0100 Subject: [PATCH 350/524] rustc_tools_util: changelog and 0.3.0 release --- Cargo.toml | 4 ++-- rustc_tools_util/CHANGELOG.md | 6 ++++++ rustc_tools_util/Cargo.toml | 2 +- rustc_tools_util/README.md | 7 +++++-- rustc_tools_util/src/lib.rs | 12 +++++------- 5 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 rustc_tools_util/CHANGELOG.md diff --git a/Cargo.toml b/Cargo.toml index 698ff035a861..4400f4c0aadb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ path = "src/driver.rs" [dependencies] clippy_lints = { path = "clippy_lints" } semver = "1.0" -rustc_tools_util = {version = "0.2.1", path = "./rustc_tools_util"} +rustc_tools_util = "0.3.0" tempfile = { version = "3.2", optional = true } termize = "0.1" @@ -56,7 +56,7 @@ tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" [build-dependencies] -rustc_tools_util = {version = "0.2.1", path = "./rustc_tools_util"} +rustc_tools_util = "0.3.0" [features] deny-warnings = ["clippy_lints/deny-warnings"] diff --git a/rustc_tools_util/CHANGELOG.md b/rustc_tools_util/CHANGELOG.md new file mode 100644 index 000000000000..1b351da2e7bc --- /dev/null +++ b/rustc_tools_util/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +## Version 0.3.0 + +* Added `setup_version_info!();` macro for automated scripts. +* `get_version_info!()` no longer requires the user to import `rustc_tools_util::VersionInfo` and `std::env` diff --git a/rustc_tools_util/Cargo.toml b/rustc_tools_util/Cargo.toml index 89c3d6aaa89e..877049ae7d0e 100644 --- a/rustc_tools_util/Cargo.toml +++ b/rustc_tools_util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustc_tools_util" -version = "0.2.1" +version = "0.3.0" description = "small helper to generate version information for git packages" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index 6204ca174f35..eefc661f9635 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -13,10 +13,10 @@ build = "build.rs" List rustc_tools_util as regular AND build dependency. ````toml [dependencies] -rustc_tools_util = "0.2.1" +rustc_tools_util = "0.3.0" [build-dependencies] -rustc_tools_util = "0.2.1" +rustc_tools_util = "0.3.0" ```` In `build.rs`, generate the data in your `main()` @@ -44,6 +44,9 @@ This gives the following output in clippy: This project is part of the rust-lang/rust-clippy repository. The source code can be found under `./rustc_tools_util/`. +The changelog for `rustc_tools_util` is available under: +[`rustc_tools_util/CHANGELOG.md`](https://github.com/rust-lang/rust-clippy/blob/master/rustc_tools_util/CHANGELOG.md) + ## License Copyright 2014-2022 The Rust Project Developers diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index 5e856319c886..4c1d8c3733df 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -1,7 +1,5 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] -use std::env; - /// This macro creates the version string during compilation from the /// current environment #[macro_export] @@ -121,7 +119,7 @@ pub fn get_commit_date() -> Option { #[must_use] pub fn get_channel() -> String { - match env::var("CFG_RELEASE_CHANNEL") { + match std::env::var("CFG_RELEASE_CHANNEL") { Ok(channel) => channel, Err(_) => { // if that failed, try to ask rustc -V, do some parsing and find out @@ -156,8 +154,8 @@ mod test { fn test_struct_local() { let vi = get_version_info!(); assert_eq!(vi.major, 0); - assert_eq!(vi.minor, 2); - assert_eq!(vi.patch, 1); + assert_eq!(vi.minor, 3); + assert_eq!(vi.patch, 0); assert_eq!(vi.crate_name, "rustc_tools_util"); // hard to make positive tests for these since they will always change assert!(vi.commit_hash.is_none()); @@ -167,7 +165,7 @@ mod test { #[test] fn test_display_local() { let vi = get_version_info!(); - assert_eq!(vi.to_string(), "rustc_tools_util 0.2.1"); + assert_eq!(vi.to_string(), "rustc_tools_util 0.3.0"); } #[test] @@ -176,7 +174,7 @@ mod test { let s = format!("{vi:?}"); assert_eq!( s, - "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 1 }" + "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 3, patch: 0 }" ); } } From 7574c9837199aebc4f3d8757cb2999a4f3604659 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 15 Dec 2022 18:51:18 +0000 Subject: [PATCH 351/524] Fix new_return_no_self with recursive bounds --- clippy_utils/src/ty.rs | 90 +++++++++++++++++++-------------- tests/ui/new_ret_no_self.rs | 23 +++++++++ tests/ui/new_ret_no_self.stderr | 42 ++++++++++----- 3 files changed, 105 insertions(+), 50 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 81b08ae5600d..6ba916306eeb 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -69,50 +69,66 @@ pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool { /// This method also recurses into opaque type predicates, so call it with `impl Trait` and `U` /// will also return `true`. pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool { - ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => { - if inner_ty == needle { - return true; - } + fn contains_ty_adt_constructor_opaque_inner<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + needle: Ty<'tcx>, + seen: &mut FxHashSet, + ) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => { + if inner_ty == needle { + return true; + } - if inner_ty.ty_adt_def() == needle.ty_adt_def() { - return true; - } + if inner_ty.ty_adt_def() == needle.ty_adt_def() { + return true; + } + + if let ty::Opaque(def_id, _) = *inner_ty.kind() { + if !seen.insert(def_id) { + return false; + } - if let ty::Opaque(def_id, _) = *inner_ty.kind() { - for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { - match predicate.kind().skip_binder() { - // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through - // and check substituions to find `U`. - ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { - if trait_predicate - .trait_ref - .substs - .types() - .skip(1) // Skip the implicit `Self` generic parameter - .any(|ty| contains_ty_adt_constructor_opaque(cx, ty, needle)) - { - return true; - } - }, - // For `impl Trait`, it will register a predicate of `::Assoc = U`, - // so we check the term for `U`. - ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => { - if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() { - if contains_ty_adt_constructor_opaque(cx, ty, needle) { + for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { + match predicate.kind().skip_binder() { + // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through + // and check substituions to find `U`. + ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { + if trait_predicate + .trait_ref + .substs + .types() + .skip(1) // Skip the implicit `Self` generic parameter + .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)) + { return true; } - }; - }, - _ => (), + }, + // For `impl Trait`, it will register a predicate of `::Assoc = U`, + // so we check the term for `U`. + ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => { + if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() { + if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) { + return true; + } + }; + }, + _ => (), + } } } - } - false - }, - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }) + false + }, + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) + } + + // A hash set to ensure that the same opaque type (`impl Trait` in RPIT or TAIT) is not + // visited twice. + let mut seen = FxHashSet::default(); + contains_ty_adt_constructor_opaque_inner(cx, ty, needle, &mut seen) } /// Resolves `::Item` for `T` diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index f69982d63a89..beec42f08bb0 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -1,3 +1,4 @@ +#![feature(type_alias_impl_trait)] #![warn(clippy::new_ret_no_self)] #![allow(dead_code)] @@ -400,3 +401,25 @@ mod issue7344 { } } } + +mod issue10041 { + struct Bomb; + + impl Bomb { + // Hidden default generic paramter. + pub fn new() -> impl PartialOrd { + 0i32 + } + } + + // TAIT with self-referencing bounds + type X = impl std::ops::Add; + + struct Bomb2; + + impl Bomb2 { + pub fn new() -> X { + 0i32 + } + } +} diff --git a/tests/ui/new_ret_no_self.stderr b/tests/ui/new_ret_no_self.stderr index bc13be47927b..2eaebfb5cac5 100644 --- a/tests/ui/new_ret_no_self.stderr +++ b/tests/ui/new_ret_no_self.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:49:5 + --> $DIR/new_ret_no_self.rs:50:5 | LL | / pub fn new(_: String) -> impl R { LL | | S3 @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:81:5 + --> $DIR/new_ret_no_self.rs:82:5 | LL | / pub fn new() -> u32 { LL | | unimplemented!(); @@ -17,7 +17,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:90:5 + --> $DIR/new_ret_no_self.rs:91:5 | LL | / pub fn new(_: String) -> u32 { LL | | unimplemented!(); @@ -25,7 +25,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:126:5 + --> $DIR/new_ret_no_self.rs:127:5 | LL | / pub fn new() -> (u32, u32) { LL | | unimplemented!(); @@ -33,7 +33,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:153:5 + --> $DIR/new_ret_no_self.rs:154:5 | LL | / pub fn new() -> *mut V { LL | | unimplemented!(); @@ -41,7 +41,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:171:5 + --> $DIR/new_ret_no_self.rs:172:5 | LL | / pub fn new() -> Option { LL | | unimplemented!(); @@ -49,19 +49,19 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:224:9 + --> $DIR/new_ret_no_self.rs:225:9 | LL | fn new() -> String; | ^^^^^^^^^^^^^^^^^^^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:236:9 + --> $DIR/new_ret_no_self.rs:237:9 | LL | fn new(_: String) -> String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:271:9 + --> $DIR/new_ret_no_self.rs:272:9 | LL | / fn new() -> (u32, u32) { LL | | unimplemented!(); @@ -69,7 +69,7 @@ LL | | } | |_________^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:298:9 + --> $DIR/new_ret_no_self.rs:299:9 | LL | / fn new() -> *mut V { LL | | unimplemented!(); @@ -77,7 +77,7 @@ LL | | } | |_________^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:368:9 + --> $DIR/new_ret_no_self.rs:369:9 | LL | / fn new(t: T) -> impl Into { LL | | 1 @@ -85,12 +85,28 @@ LL | | } | |_________^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:389:9 + --> $DIR/new_ret_no_self.rs:390:9 | LL | / fn new(t: T) -> impl Trait2<(), i32> { LL | | unimplemented!() LL | | } | |_________^ -error: aborting due to 12 previous errors +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:410:9 + | +LL | / pub fn new() -> impl PartialOrd { +LL | | 0i32 +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:421:9 + | +LL | / pub fn new() -> X { +LL | | 0i32 +LL | | } + | |_________^ + +error: aborting due to 14 previous errors From 5b3a6669f75dda31d9f736abb0ef30429264069a Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Thu, 15 Dec 2022 23:29:28 -0500 Subject: [PATCH 352/524] fix manual_filter false positive do explicit checks for the other branch being None --- clippy_lints/src/matches/manual_filter.rs | 17 +++++++++----- tests/ui/manual_filter.fixed | 27 +++++++++++++++++++++++ tests/ui/manual_filter.rs | 27 +++++++++++++++++++++++ 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index d521a529e0d6..f6bf0e7aa1ad 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; -use rustc_hir::LangItem::OptionSome; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::{sym, SyntaxContext}; @@ -25,15 +25,13 @@ fn get_cond_expr<'tcx>( if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; if let PatKind::Binding(_,target, ..) = pat.kind; - if let (then_visitor, else_visitor) - = (is_some_expr(cx, target, ctxt, then_expr), - is_some_expr(cx, target, ctxt, else_expr)); - if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` + if is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr) + || is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr); // check that one expr resolves to `Some(x)`, the other to `None` then { return Some(SomeExpr { expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), needs_unsafe_block: contains_unsafe_block(cx, expr), - needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond + needs_negated: is_none_expr(cx, then_expr) // if the `then_expr` resolves to `None`, need to negate the cond }) } }; @@ -74,6 +72,13 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: false } +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { + return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); + }; + false +} + // given the closure: `|| ` // returns `|&| ` fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String { diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed index 3553291b87df..b986dd5e2130 100644 --- a/tests/ui/manual_filter.fixed +++ b/tests/ui/manual_filter.fixed @@ -116,4 +116,31 @@ fn main() { }, None => None, }; + + match Some(20) { + // Don't Lint, because `Some(3*x)` is not `None` + None => None, + Some(x) => { + if x > 0 { + Some(3 * x) + } else { + Some(x) + } + }, + }; + + // Don't lint: https://github.com/rust-lang/rust-clippy/issues/10088 + let result = if let Some(a) = maybe_some() { + if let Some(b) = maybe_some() { + Some(a + b) + } else { + Some(a) + } + } else { + None + }; +} + +fn maybe_some() -> Option { + Some(0) } diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs index aa9f90f752b1..1e786b9027cc 100644 --- a/tests/ui/manual_filter.rs +++ b/tests/ui/manual_filter.rs @@ -240,4 +240,31 @@ fn main() { }, None => None, }; + + match Some(20) { + // Don't Lint, because `Some(3*x)` is not `None` + None => None, + Some(x) => { + if x > 0 { + Some(3 * x) + } else { + Some(x) + } + }, + }; + + // Don't lint: https://github.com/rust-lang/rust-clippy/issues/10088 + let result = if let Some(a) = maybe_some() { + if let Some(b) = maybe_some() { + Some(a + b) + } else { + Some(a) + } + } else { + None + }; +} + +fn maybe_some() -> Option { + Some(0) } From 3c14075b465bdfa7e6b402ebba8cac6766b3dcdf Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Fri, 16 Dec 2022 09:29:32 -0500 Subject: [PATCH 353/524] manual_filter: add a related test The related issue is #9766 where the `manual_filter` lint would remove side effects --- tests/ui/manual_filter.fixed | 14 ++++++++++++++ tests/ui/manual_filter.rs | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed index b986dd5e2130..ef6780dc96d9 100644 --- a/tests/ui/manual_filter.fixed +++ b/tests/ui/manual_filter.fixed @@ -139,6 +139,20 @@ fn main() { } else { None }; + + let allowed_integers = vec![3, 4, 5, 6]; + // Don't lint, since there's a side effect in the else branch + match Some(21) { + Some(x) => { + if allowed_integers.contains(&x) { + Some(x) + } else { + println!("Invalid integer: {x:?}"); + None + } + }, + None => None, + }; } fn maybe_some() -> Option { diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs index 1e786b9027cc..ea0ce83172b7 100644 --- a/tests/ui/manual_filter.rs +++ b/tests/ui/manual_filter.rs @@ -263,6 +263,20 @@ fn main() { } else { None }; + + let allowed_integers = vec![3, 4, 5, 6]; + // Don't lint, since there's a side effect in the else branch + match Some(21) { + Some(x) => { + if allowed_integers.contains(&x) { + Some(x) + } else { + println!("Invalid integer: {x:?}"); + None + } + }, + None => None, + }; } fn maybe_some() -> Option { From 97c12e0460270bd6b0a6d52b8677f24c5b2d6879 Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Fri, 16 Dec 2022 10:22:29 -0500 Subject: [PATCH 354/524] fix logic in IncrementVisitor There used to be a logical bug where IncrementVisitor would completely stop checking an expression/block after seeing a continue statement. This led to issue #10058 where a variable incremented (or otherwise modified) after any continue statement would still be considered incremented only once. The solution is to continue scanning the expression after seeing a `continue` statement, but increment self.depth so that the Visitor thinks that the rest of the loop is within a conditional. --- clippy_lints/src/loops/utils.rs | 10 +++------- tests/ui/explicit_counter_loop.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index b6f4cf7bbb37..28ee24309cc4 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -25,7 +25,6 @@ pub(super) struct IncrementVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference states: HirIdMap, // incremented variables depth: u32, // depth of conditional expressions - done: bool, } impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { @@ -34,7 +33,6 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { cx, states: HirIdMap::default(), depth: 0, - done: false, } } @@ -51,10 +49,6 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.done { - return; - } - // If node is a variable if let Some(def_id) = path_to_local(expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { @@ -95,7 +89,9 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { walk_expr(self, expr); self.depth -= 1; } else if let ExprKind::Continue(_) = expr.kind { - self.done = true; + // If we see a `continue` block, then we increment depth so that the IncrementVisitor + // state will be set to DontWarn if we see the variable being modified anywhere afterwards. + self.depth += 1; } else { walk_expr(self, expr); } diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index 6eddc01e2c47..46565a97f005 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -189,3 +189,33 @@ mod issue_7920 { } } } + +mod issue_10058 { + pub fn test() { + // should not lint since we are increasing counter potentially more than once in the loop + let values = [0, 1, 0, 1, 1, 1, 0, 1, 0, 1]; + let mut counter = 0; + for value in values { + counter += 1; + + if value == 0 { + continue; + } + + counter += 1; + } + } + + pub fn test2() { + // should not lint since we are increasing counter potentially more than once in the loop + let values = [0, 1, 0, 1, 1, 1, 0, 1, 0, 1]; + let mut counter = 0; + for value in values { + counter += 1; + + if value != 0 { + counter += 1; + } + } + } +} From 033f1ec7975a920aac0c33da9a5e4af0f699b7a0 Mon Sep 17 00:00:00 2001 From: chansuke Date: Tue, 13 Dec 2022 01:18:44 +0900 Subject: [PATCH 355/524] Add 2018/2021 edition tests for wildcard_imports --- tests/ui/wildcard_imports.fixed | 1 - tests/ui/wildcard_imports.rs | 1 - tests/ui/wildcard_imports.stderr | 42 +-- .../wildcard_imports_2021.edition2018.fixed | 240 +++++++++++++++++ .../wildcard_imports_2021.edition2018.stderr | 132 ++++++++++ .../wildcard_imports_2021.edition2021.fixed | 240 +++++++++++++++++ .../wildcard_imports_2021.edition2021.stderr | 132 ++++++++++ tests/ui/wildcard_imports_2021.rs | 241 ++++++++++++++++++ tests/ui/wildcard_imports_2021.stderr | 132 ++++++++++ 9 files changed, 1138 insertions(+), 23 deletions(-) create mode 100644 tests/ui/wildcard_imports_2021.edition2018.fixed create mode 100644 tests/ui/wildcard_imports_2021.edition2018.stderr create mode 100644 tests/ui/wildcard_imports_2021.edition2021.fixed create mode 100644 tests/ui/wildcard_imports_2021.edition2021.stderr create mode 100644 tests/ui/wildcard_imports_2021.rs create mode 100644 tests/ui/wildcard_imports_2021.stderr diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index ef55f1c31a88..0baec6f0b641 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -5,7 +5,6 @@ // the 2015 edition here is needed because edition 2018 changed the module system // (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint // no longer detects some of the cases starting with Rust 2018. -// FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] #![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index b81285142069..db591d56ab4d 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -5,7 +5,6 @@ // the 2015 edition here is needed because edition 2018 changed the module system // (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint // no longer detects some of the cases starting with Rust 2018. -// FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] #![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 626c1754fc82..6b469cdfc444 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:16:5 + --> $DIR/wildcard_imports.rs:15:5 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` @@ -7,85 +7,85 @@ LL | use crate::fn_mod::*; = note: `-D clippy::wildcard-imports` implied by `-D warnings` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:17:5 + --> $DIR/wildcard_imports.rs:16:5 | LL | use crate::mod_mod::*; | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:18:5 + --> $DIR/wildcard_imports.rs:17:5 | LL | use crate::multi_fn_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:20:5 + --> $DIR/wildcard_imports.rs:19:5 | LL | use crate::struct_mod::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:24:5 + --> $DIR/wildcard_imports.rs:23:5 | LL | use wildcard_imports_helper::inner::inner_for_self_import::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:25:5 + --> $DIR/wildcard_imports.rs:24:5 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:96:13 + --> $DIR/wildcard_imports.rs:95:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:102:75 + --> $DIR/wildcard_imports.rs:101:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:103:13 + --> $DIR/wildcard_imports.rs:102:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:20 + --> $DIR/wildcard_imports.rs:113:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:30 + --> $DIR/wildcard_imports.rs:113:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:121:13 + --> $DIR/wildcard_imports.rs:120:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:150:9 + --> $DIR/wildcard_imports.rs:149:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:159:9 + --> $DIR/wildcard_imports.rs:158:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:160:9 + --> $DIR/wildcard_imports.rs:159:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:171:13 + --> $DIR/wildcard_imports.rs:170:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:206:17 + --> $DIR/wildcard_imports.rs:205:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:214:13 + --> $DIR/wildcard_imports.rs:213:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:223:17 + --> $DIR/wildcard_imports.rs:222:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:232:13 + --> $DIR/wildcard_imports.rs:231:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:240:13 + --> $DIR/wildcard_imports.rs:239:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wildcard_imports_2021.edition2018.fixed b/tests/ui/wildcard_imports_2021.edition2018.fixed new file mode 100644 index 000000000000..6d534a10edcd --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -0,0 +1,240 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::foo; +use crate::mod_mod::inner_mod; +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::struct_mod::{A, inner_struct_mod}; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::{ExternA, extern_foo}; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::foo; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; + use wildcard_imports_helper::{ExternA, extern_foo}; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::inner_foo, inner2::inner_bar}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test::exported; + use crate:: fn_mod::foo; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.edition2018.stderr b/tests/ui/wildcard_imports_2021.edition2018.stderr new file mode 100644 index 000000000000..acca9f651b47 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:13:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:14:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:16:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:19:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:21:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:91:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:97:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:98:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:116:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:145:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:154:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:155:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:166:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:201:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:209:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:218:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:227:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:235:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + diff --git a/tests/ui/wildcard_imports_2021.edition2021.fixed b/tests/ui/wildcard_imports_2021.edition2021.fixed new file mode 100644 index 000000000000..6d534a10edcd --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -0,0 +1,240 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::foo; +use crate::mod_mod::inner_mod; +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::struct_mod::{A, inner_struct_mod}; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::{ExternA, extern_foo}; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::foo; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; + use wildcard_imports_helper::{ExternA, extern_foo}; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::inner_foo, inner2::inner_bar}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test::exported; + use crate:: fn_mod::foo; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.edition2021.stderr b/tests/ui/wildcard_imports_2021.edition2021.stderr new file mode 100644 index 000000000000..acca9f651b47 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:13:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:14:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:16:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:19:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:21:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:91:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:97:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:98:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:116:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:145:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:154:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:155:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:166:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:201:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:209:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:218:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:227:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:235:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + diff --git a/tests/ui/wildcard_imports_2021.rs b/tests/ui/wildcard_imports_2021.rs new file mode 100644 index 000000000000..b5ed58e68136 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.rs @@ -0,0 +1,241 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::*; +use crate::mod_mod::*; +use crate::multi_fn_mod::*; +use crate::struct_mod::*; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::*; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::*; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::*; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + use wildcard_imports_helper::*; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::*, inner2::*}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::*; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::*; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test:: * ; + use crate:: fn_mod:: + *; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::*; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::*; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::*; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::*; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.stderr b/tests/ui/wildcard_imports_2021.stderr new file mode 100644 index 000000000000..92f6d31530fa --- /dev/null +++ b/tests/ui/wildcard_imports_2021.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:9:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:10:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:11:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:12:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:17:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:87:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:93:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:94:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:105:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:105:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:112:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:141:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:150:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:151:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:162:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:197:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:205:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:214:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:223:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:231:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + From c39849a34dae51d104f197894ed5a9f02dd62d75 Mon Sep 17 00:00:00 2001 From: feniljain Date: Sat, 17 Dec 2022 17:24:03 +0530 Subject: [PATCH 356/524] fix: not suggest seek_to_start_instead_of_rewind when expr is used --- clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs | 6 +++++- tests/ui/seek_to_start_instead_of_rewind.fixed | 6 ++++++ tests/ui/seek_to_start_instead_of_rewind.rs | 6 ++++++ tests/ui/seek_to_start_instead_of_rewind.stderr | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs index 7e3bed1e41a9..660b7049cce9 100644 --- a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs +++ b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, match_def_path, paths}; +use clippy_utils::{get_trait_def_id, is_expr_used_or_unified, match_def_path, paths}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -19,6 +19,10 @@ pub(super) fn check<'tcx>( // Get receiver type let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + if is_expr_used_or_unified(cx.tcx, expr) { + return; + } + if let Some(seek_trait_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) && implements_trait(cx, ty, seek_trait_id, &[]) && let ExprKind::Call(func, args1) = arg.kind && diff --git a/tests/ui/seek_to_start_instead_of_rewind.fixed b/tests/ui/seek_to_start_instead_of_rewind.fixed index 9d0d1124c460..713cff604a1d 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.fixed +++ b/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -70,6 +70,12 @@ fn seek_to_end(t: &mut T) { t.seek(SeekFrom::End(0)); } +// This should NOT trigger clippy warning because +// expr is used here +fn seek_to_start_in_let(t: &mut T) { + let a = t.seek(SeekFrom::Start(0)).unwrap(); +} + fn main() { let mut f = OpenOptions::new() .write(true) diff --git a/tests/ui/seek_to_start_instead_of_rewind.rs b/tests/ui/seek_to_start_instead_of_rewind.rs index c5bc57cc3a74..467003a1a66f 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.rs +++ b/tests/ui/seek_to_start_instead_of_rewind.rs @@ -70,6 +70,12 @@ fn seek_to_end(t: &mut T) { t.seek(SeekFrom::End(0)); } +// This should NOT trigger clippy warning because +// expr is used here +fn seek_to_start_in_let(t: &mut T) { + let a = t.seek(SeekFrom::Start(0)).unwrap(); +} + fn main() { let mut f = OpenOptions::new() .write(true) diff --git a/tests/ui/seek_to_start_instead_of_rewind.stderr b/tests/ui/seek_to_start_instead_of_rewind.stderr index 6cce025359fe..342ec00fe729 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.stderr +++ b/tests/ui/seek_to_start_instead_of_rewind.stderr @@ -13,7 +13,7 @@ LL | t.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` error: used `seek` to go to the start of the stream - --> $DIR/seek_to_start_instead_of_rewind.rs:128:7 + --> $DIR/seek_to_start_instead_of_rewind.rs:134:7 | LL | f.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` From 1f1d23cf5162e621132e069fc48d5167364be0df Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 17 Dec 2022 13:57:41 +0100 Subject: [PATCH 357/524] Bump Clippy version -> 0.1.68 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4400f4c0aadb..e1b15cc49da4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.67" +version = "0.1.68" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index dcadd012a44d..38a87017635b 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.67" +version = "0.1.68" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index fb9f4740ecc5..ac6a566b9cd3 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.67" +version = "0.1.68" edition = "2021" publish = false diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 082570f1fe5d..c01e1062cb54 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.67" +version = "0.1.68" edition = "2021" publish = false From 7198989d45e4e47ccc30077c522468cac172766c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 17 Dec 2022 13:57:48 +0100 Subject: [PATCH 358/524] Bump nightly version -> 2022-12-17 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 19fee38db46e..8e21cef32abb 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-12-01" +channel = "nightly-2022-12-17" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 1c422524c70ef710033014edce8cf53f55c39e49 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 17 Dec 2022 14:12:54 +0100 Subject: [PATCH 359/524] Merge commit '4bdfb0741dbcecd5279a2635c3280726db0604b5' into clippyup --- .github/workflows/clippy_bors.yml | 7 - CHANGELOG.md | 177 ++++++++++++- Cargo.toml | 6 +- book/src/README.md | 2 +- build.rs | 14 +- clippy_lints/Cargo.toml | 2 +- ...tter_range.rs => almost_complete_range.rs} | 24 +- clippy_lints/src/box_default.rs | 2 +- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/declared_lints.rs | 4 +- clippy_lints/src/dereference.rs | 11 +- clippy_lints/src/derive.rs | 5 +- clippy_lints/src/disallowed_macros.rs | 2 +- clippy_lints/src/format_args.rs | 10 +- clippy_lints/src/from_over_into.rs | 3 +- clippy_lints/src/implicit_saturating_add.rs | 2 +- clippy_lints/src/indexing_slicing.rs | 45 +++- clippy_lints/src/len_zero.rs | 20 +- clippy_lints/src/lib.rs | 28 ++- clippy_lints/src/loops/utils.rs | 10 +- clippy_lints/src/manual_assert.rs | 6 +- clippy_lints/src/manual_is_ascii_check.rs | 91 ++++--- clippy_lints/src/manual_let_else.rs | 7 +- clippy_lints/src/manual_retain.rs | 3 +- clippy_lints/src/methods/implicit_clone.rs | 4 +- clippy_lints/src/methods/mod.rs | 7 +- .../seek_to_start_instead_of_rewind.rs | 6 +- clippy_lints/src/misc.rs | 66 +++-- .../src/operators/arithmetic_side_effects.rs | 74 ++++-- clippy_lints/src/operators/identity_op.rs | 74 +++++- clippy_lints/src/operators/mod.rs | 3 - clippy_lints/src/redundant_pub_crate.rs | 6 +- .../src/redundant_static_lifetimes.rs | 2 +- clippy_lints/src/renamed_lints.rs | 1 + clippy_lints/src/semicolon_block.rs | 137 ++++++++++ clippy_lints/src/strings.rs | 38 +-- clippy_lints/src/utils/conf.rs | 51 +++- .../src/utils/internal_lints/invalid_paths.rs | 10 +- clippy_utils/Cargo.toml | 2 +- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/macros.rs | 6 + clippy_utils/src/msrvs.rs | 2 +- clippy_utils/src/paths.rs | 1 - clippy_utils/src/qualify_min_const_fn.rs | 5 +- clippy_utils/src/ty.rs | 108 ++++---- declare_clippy_lint/Cargo.toml | 2 +- rust-toolchain | 2 +- rustc_tools_util/CHANGELOG.md | 6 + rustc_tools_util/Cargo.toml | 2 +- rustc_tools_util/README.md | 38 ++- rustc_tools_util/src/lib.rs | 48 ++-- src/driver.rs | 1 - src/main.rs | 1 - ...unnecessary_def_path_hardcoded_path.stderr | 16 +- .../arithmetic_side_effects_allowed.rs | 123 +++++++-- .../arithmetic_side_effects_allowed.stderr | 58 +++++ .../clippy.toml | 12 +- .../suppress_lint_in_const/clippy.toml | 1 + tests/ui-toml/suppress_lint_in_const/test.rs | 60 +++++ .../suppress_lint_in_const/test.stderr | 70 ++++++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui/almost_complete_letter_range.stderr | 113 --------- ...ange.fixed => almost_complete_range.fixed} | 49 +++- ...tter_range.rs => almost_complete_range.rs} | 49 +++- tests/ui/almost_complete_range.stderr | 235 ++++++++++++++++++ tests/ui/arithmetic_side_effects.stderr | 24 +- tests/ui/auxiliary/macro_rules.rs | 4 +- tests/ui/cast_lossless_integer.fixed | 6 + tests/ui/cast_lossless_integer.rs | 6 + tests/ui/collapsible_str_replace.fixed | 11 + tests/ui/collapsible_str_replace.rs | 11 + tests/ui/collapsible_str_replace.stderr | 34 +-- tests/ui/explicit_counter_loop.rs | 30 +++ tests/ui/from_over_into.fixed | 7 + tests/ui/from_over_into.rs | 7 + tests/ui/from_over_into.stderr | 10 +- tests/ui/identity_op.fixed | 6 +- tests/ui/identity_op.rs | 4 + tests/ui/identity_op.stderr | 12 +- tests/ui/implicit_clone.fixed | 10 + tests/ui/implicit_clone.rs | 10 + tests/ui/indexing_slicing_index.rs | 6 +- tests/ui/indexing_slicing_index.stderr | 44 +++- tests/ui/len_zero.fixed | 40 +++ tests/ui/len_zero.rs | 40 +++ tests/ui/len_zero.stderr | 74 ++++-- tests/ui/manual_assert.edition2018.fixed | 5 + tests/ui/manual_assert.edition2021.fixed | 5 + tests/ui/manual_assert.edition2021.stderr | 2 +- tests/ui/manual_assert.rs | 5 + tests/ui/manual_is_ascii_check.fixed | 13 + tests/ui/manual_is_ascii_check.rs | 13 + tests/ui/manual_is_ascii_check.stderr | 64 ++++- tests/ui/manual_let_else_match.rs | 7 + tests/ui/manual_let_else_match.stderr | 15 +- .../needless_parens_on_range_literals.fixed | 2 +- tests/ui/needless_parens_on_range_literals.rs | 2 +- tests/ui/new_ret_no_self.rs | 23 ++ tests/ui/new_ret_no_self.stderr | 42 +++- tests/ui/redundant_static_lifetimes.fixed | 6 + tests/ui/redundant_static_lifetimes.rs | 6 + tests/ui/redundant_static_lifetimes.stderr | 10 +- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 92 +++---- .../ui/seek_to_start_instead_of_rewind.fixed | 6 + tests/ui/seek_to_start_instead_of_rewind.rs | 6 + .../ui/seek_to_start_instead_of_rewind.stderr | 2 +- tests/ui/semicolon_inside_block.fixed | 85 +++++++ tests/ui/semicolon_inside_block.rs | 85 +++++++ tests/ui/semicolon_inside_block.stderr | 54 ++++ tests/ui/semicolon_outside_block.fixed | 85 +++++++ tests/ui/semicolon_outside_block.rs | 85 +++++++ tests/ui/semicolon_outside_block.stderr | 54 ++++ tests/ui/string_lit_as_bytes.fixed | 6 + tests/ui/string_lit_as_bytes.rs | 6 + ...nlined_format_args_panic.edition2018.fixed | 3 + ...nlined_format_args_panic.edition2021.fixed | 3 + ...lined_format_args_panic.edition2021.stderr | 26 +- tests/ui/uninlined_format_args_panic.rs | 3 + tests/ui/unnecessary_to_owned.fixed | 20 ++ tests/ui/unnecessary_to_owned.rs | 20 ++ tests/ui/zero_ptr_no_std.fixed | 21 ++ tests/ui/zero_ptr_no_std.rs | 21 ++ tests/ui/zero_ptr_no_std.stderr | 26 ++ tests/versioncheck.rs | 1 - triagebot.toml | 2 +- 127 files changed, 2707 insertions(+), 602 deletions(-) rename clippy_lints/src/{almost_complete_letter_range.rs => almost_complete_range.rs} (85%) create mode 100644 clippy_lints/src/semicolon_block.rs create mode 100644 rustc_tools_util/CHANGELOG.md create mode 100644 tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr create mode 100644 tests/ui-toml/suppress_lint_in_const/clippy.toml create mode 100644 tests/ui-toml/suppress_lint_in_const/test.rs create mode 100644 tests/ui-toml/suppress_lint_in_const/test.stderr delete mode 100644 tests/ui/almost_complete_letter_range.stderr rename tests/ui/{almost_complete_letter_range.fixed => almost_complete_range.fixed} (56%) rename tests/ui/{almost_complete_letter_range.rs => almost_complete_range.rs} (57%) create mode 100644 tests/ui/almost_complete_range.stderr create mode 100644 tests/ui/semicolon_inside_block.fixed create mode 100644 tests/ui/semicolon_inside_block.rs create mode 100644 tests/ui/semicolon_inside_block.stderr create mode 100644 tests/ui/semicolon_outside_block.fixed create mode 100644 tests/ui/semicolon_outside_block.rs create mode 100644 tests/ui/semicolon_outside_block.stderr create mode 100644 tests/ui/zero_ptr_no_std.fixed create mode 100644 tests/ui/zero_ptr_no_std.rs create mode 100644 tests/ui/zero_ptr_no_std.stderr diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 6448b2d4068d..1bc457a94793 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -82,13 +82,6 @@ jobs: with: github_token: "${{ secrets.github_token }}" - - name: Install dependencies (Linux-i686) - run: | - sudo dpkg --add-architecture i386 - sudo apt-get update - sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386 - if: matrix.host == 'i686-unknown-linux-gnu' - - name: Checkout uses: actions/checkout@v3.0.2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 23912bb3ed6b..903ee938d9d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,181 @@ document. ## Unreleased / Beta / In Rust Nightly -[b52fb523...master](https://github.com/rust-lang/rust-clippy/compare/b52fb523...master) +[4f142aa1...master](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...master) + +## Rust 1.66 + +Current stable, released 2022-12-15 + +[b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1) + +### New Lints + +* [`manual_clamp`] + [#9484](https://github.com/rust-lang/rust-clippy/pull/9484) +* [`missing_trait_methods`] + [#9670](https://github.com/rust-lang/rust-clippy/pull/9670) +* [`unused_format_specs`] + [#9637](https://github.com/rust-lang/rust-clippy/pull/9637) +* [`iter_kv_map`] + [#9409](https://github.com/rust-lang/rust-clippy/pull/9409) +* [`manual_filter`] + [#9451](https://github.com/rust-lang/rust-clippy/pull/9451) +* [`box_default`] + [#9511](https://github.com/rust-lang/rust-clippy/pull/9511) +* [`implicit_saturating_add`] + [#9549](https://github.com/rust-lang/rust-clippy/pull/9549) +* [`as_ptr_cast_mut`] + [#9572](https://github.com/rust-lang/rust-clippy/pull/9572) +* [`disallowed_macros`] + [#9495](https://github.com/rust-lang/rust-clippy/pull/9495) +* [`partial_pub_fields`] + [#9658](https://github.com/rust-lang/rust-clippy/pull/9658) +* [`uninlined_format_args`] + [#9233](https://github.com/rust-lang/rust-clippy/pull/9233) +* [`cast_nan_to_int`] + [#9617](https://github.com/rust-lang/rust-clippy/pull/9617) + +### Moves and Deprecations + +* `positional_named_format_parameters` was uplifted to rustc under the new name + `named_arguments_used_positionally` + [#8518](https://github.com/rust-lang/rust-clippy/pull/8518) +* Moved [`implicit_saturating_sub`] to `style` (Now warn-by-default) + [#9584](https://github.com/rust-lang/rust-clippy/pull/9584) +* Moved `derive_partial_eq_without_eq` to `nursery` (now allow-by-default) + [#9536](https://github.com/rust-lang/rust-clippy/pull/9536) + +### Enhancements + +* [`nonstandard_macro_braces`]: Now includes `matches!()` in the default lint config + [#9471](https://github.com/rust-lang/rust-clippy/pull/9471) +* [`suboptimal_flops`]: Now supports multiplication and subtraction operations + [#9581](https://github.com/rust-lang/rust-clippy/pull/9581) +* [`arithmetic_side_effects`]: Now detects cases with literals behind references + [#9587](https://github.com/rust-lang/rust-clippy/pull/9587) +* [`upper_case_acronyms`]: Now also checks enum names + [#9580](https://github.com/rust-lang/rust-clippy/pull/9580) +* [`needless_borrowed_reference`]: Now lints nested patterns + [#9573](https://github.com/rust-lang/rust-clippy/pull/9573) +* [`unnecessary_cast`]: Now works for non-trivial non-literal expressions + [#9576](https://github.com/rust-lang/rust-clippy/pull/9576) +* [`arithmetic_side_effects`]: Now detects operations with custom types + [#9559](https://github.com/rust-lang/rust-clippy/pull/9559) +* [`disallowed_methods`], [`disallowed_types`]: Not correctly lints types, functions and macros + with the same path + [#9495](https://github.com/rust-lang/rust-clippy/pull/9495) +* [`self_named_module_files`], [`mod_module_files`]: Now take remapped path prefixes into account + [#9475](https://github.com/rust-lang/rust-clippy/pull/9475) +* [`bool_to_int_with_if`]: Now detects the inverse if case + [#9476](https://github.com/rust-lang/rust-clippy/pull/9476) + +### False Positive Fixes + +* [`arithmetic_side_effects`]: Now allows operations that can't overflow + [#9474](https://github.com/rust-lang/rust-clippy/pull/9474) +* [`unnecessary_lazy_evaluations`]: No longer lints in external macros + [#9486](https://github.com/rust-lang/rust-clippy/pull/9486) +* [`needless_borrow`], [`explicit_auto_deref`]: No longer lint on unions that require the reference + [#9490](https://github.com/rust-lang/rust-clippy/pull/9490) +* [`almost_complete_letter_range`]: No longer lints in external macros + [#9467](https://github.com/rust-lang/rust-clippy/pull/9467) +* [`drop_copy`]: No longer lints on idiomatic cases in match arms + [#9491](https://github.com/rust-lang/rust-clippy/pull/9491) +* [`question_mark`]: No longer lints in const context + [#9487](https://github.com/rust-lang/rust-clippy/pull/9487) +* [`collapsible_if`]: Suggestion now work in macros + [#9410](https://github.com/rust-lang/rust-clippy/pull/9410) +* [`std_instead_of_core`]: No longer triggers on unstable modules + [#9545](https://github.com/rust-lang/rust-clippy/pull/9545) +* [`unused_peekable`]: No longer lints, if the peak is done in a closure or function + [#9465](https://github.com/rust-lang/rust-clippy/pull/9465) +* [`useless_attribute`]: No longer lints on `#[allow]` attributes for [`unsafe_removed_from_name`] + [#9593](https://github.com/rust-lang/rust-clippy/pull/9593) +* [`unnecessary_lazy_evaluations`]: No longer suggest switching to early evaluation when type has + custom `Drop` implementation + [#9551](https://github.com/rust-lang/rust-clippy/pull/9551) +* [`unnecessary_cast`]: No longer lints on negative hexadecimal literals when cast as floats + [#9609](https://github.com/rust-lang/rust-clippy/pull/9609) +* [`use_self`]: No longer lints in proc macros + [#9454](https://github.com/rust-lang/rust-clippy/pull/9454) +* [`never_loop`]: Now takes `let ... else` statements into consideration. + [#9496](https://github.com/rust-lang/rust-clippy/pull/9496) +* [`default_numeric_fallback`]: Now ignores constants + [#9636](https://github.com/rust-lang/rust-clippy/pull/9636) +* [`uninit_vec`]: No longer lints `Vec::set_len(0)` + [#9519](https://github.com/rust-lang/rust-clippy/pull/9519) +* [`arithmetic_side_effects`]: Now ignores references to integer types + [#9507](https://github.com/rust-lang/rust-clippy/pull/9507) +* [`large_stack_arrays`]: No longer lints inside static items + [#9466](https://github.com/rust-lang/rust-clippy/pull/9466) +* [`ref_option_ref`]: No longer lints if the inner reference is mutable + [#9684](https://github.com/rust-lang/rust-clippy/pull/9684) +* [`ptr_arg`]: No longer lints if the argument is used as an incomplete trait object + [#9645](https://github.com/rust-lang/rust-clippy/pull/9645) +* [`should_implement_trait`]: Now also works for `default` methods + [#9546](https://github.com/rust-lang/rust-clippy/pull/9546) + +### Suggestion Fixes/Improvements + +* [`derivable_impls`]: The suggestion is now machine applicable + [#9429](https://github.com/rust-lang/rust-clippy/pull/9429) +* [`match_single_binding`]: The suggestion now handles scrutinies with side effects better + [#9601](https://github.com/rust-lang/rust-clippy/pull/9601) +* [`zero_prefixed_literal`]: Only suggests using octal numbers, if this is possible + [#9652](https://github.com/rust-lang/rust-clippy/pull/9652) +* [`rc_buffer`]: The suggestion is no longer machine applicable to avoid semantic changes + [#9633](https://github.com/rust-lang/rust-clippy/pull/9633) +* [`print_literal`], [`write_literal`], [`uninlined_format_args`]: The suggestion now ignores + comments after the macro call. + [#9586](https://github.com/rust-lang/rust-clippy/pull/9586) +* [`expect_fun_call`]:Improved the suggestion for `format!` calls with captured variables + [#9586](https://github.com/rust-lang/rust-clippy/pull/9586) +* [`nonstandard_macro_braces`]: The suggestion is now machine applicable and will no longer + replace brackets inside the macro argument. + [#9499](https://github.com/rust-lang/rust-clippy/pull/9499) +* [`from_over_into`]: The suggestion is now a machine applicable and contains explanations + [#9649](https://github.com/rust-lang/rust-clippy/pull/9649) +* [`needless_return`]: The automatic suggestion now removes all required semicolons + [#9497](https://github.com/rust-lang/rust-clippy/pull/9497) +* [`to_string_in_format_args`]: The suggestion now keeps parenthesis around values + [#9590](https://github.com/rust-lang/rust-clippy/pull/9590) +* [`manual_assert`]: The suggestion now preserves comments + [#9479](https://github.com/rust-lang/rust-clippy/pull/9479) +* [`redundant_allocation`]: The suggestion applicability is now marked `MaybeIncorrect` to + avoid semantic changes + [#9634](https://github.com/rust-lang/rust-clippy/pull/9634) +* [`assertions_on_result_states`]: The suggestion has been corrected, for cases where the + `assert!` is not in a statement. + [#9453](https://github.com/rust-lang/rust-clippy/pull/9453) +* [`nonminimal_bool`]: The suggestion no longer expands macros + [#9457](https://github.com/rust-lang/rust-clippy/pull/9457) +* [`collapsible_match`]: Now specifies field names, when a struct is destructed + [#9685](https://github.com/rust-lang/rust-clippy/pull/9685) +* [`unnecessary_cast`]: The suggestion now adds parenthesis for negative numbers + [#9577](https://github.com/rust-lang/rust-clippy/pull/9577) +* [`redundant_closure`]: The suggestion now works for `impl FnMut` arguments + [#9556](https://github.com/rust-lang/rust-clippy/pull/9556) + +### ICE Fixes + +* [`unnecessary_to_owned`]: Avoid ICEs in favor of false negatives if information is missing + [#9505](https://github.com/rust-lang/rust-clippy/pull/9505) +* [`manual_range_contains`]: No longer ICEs on values behind references + [#9627](https://github.com/rust-lang/rust-clippy/pull/9627) +* [`needless_pass_by_value`]: No longer ICEs on unsized `dyn Fn` arguments + [#9531](https://github.com/rust-lang/rust-clippy/pull/9531) +* `*_interior_mutable_const` lints: no longer ICE on const unions containing `!Freeze` types + [#9539](https://github.com/rust-lang/rust-clippy/pull/9539) + +### Others + +* Released `rustc_tools_util` for version information on `Crates.io`. (Further adjustments will + not be published as part of this changelog) ## Rust 1.65 -Current stable, released 2022-11-03 +Released 2022-11-03 [3c7e7dbc...b52fb523](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...b52fb523) @@ -3875,6 +4045,7 @@ Released 2018-09-13 [`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core [`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason [`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range +[`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects @@ -4353,6 +4524,8 @@ Released 2018-09-13 [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors [`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned +[`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block +[`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block [`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse diff --git a/Cargo.toml b/Cargo.toml index fe425a2fb991..f8cb4b7219c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.67" +version = "0.1.68" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -23,7 +23,7 @@ path = "src/driver.rs" [dependencies] clippy_lints = { path = "clippy_lints" } semver = "1.0" -rustc_tools_util = "0.2.1" +rustc_tools_util = "0.3.0" tempfile = { version = "3.2", optional = true } termize = "0.1" @@ -55,7 +55,7 @@ tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" [build-dependencies] -rustc_tools_util = "0.2.1" +rustc_tools_util = "0.3.0" [features] deny-warnings = ["clippy_lints/deny-warnings"] diff --git a/book/src/README.md b/book/src/README.md index 6248d588a890..23867df8efe1 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -1,6 +1,6 @@ # Clippy -[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto) +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test%20(bors)/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto) [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license) A collection of lints to catch common mistakes and improve your diff --git a/build.rs b/build.rs index b5484bec3c8b..b79d09b0dd2d 100644 --- a/build.rs +++ b/build.rs @@ -3,17 +3,5 @@ fn main() { println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap()); // Don't rebuild even if nothing changed println!("cargo:rerun-if-changed=build.rs"); - // forward git repo hashes we build at - println!( - "cargo:rustc-env=GIT_HASH={}", - rustc_tools_util::get_commit_hash().unwrap_or_default() - ); - println!( - "cargo:rustc-env=COMMIT_DATE={}", - rustc_tools_util::get_commit_date().unwrap_or_default() - ); - println!( - "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", - rustc_tools_util::get_channel() - ); + rustc_tools_util::setup_version_info!(); } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index aedff24c12c6..a9f69b1ba630 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.67" +version = "0.1.68" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_range.rs similarity index 85% rename from clippy_lints/src/almost_complete_letter_range.rs rename to clippy_lints/src/almost_complete_range.rs index 52beaf504a4e..42e14b5cd945 100644 --- a/clippy_lints/src/almost_complete_letter_range.rs +++ b/clippy_lints/src/almost_complete_range.rs @@ -10,8 +10,8 @@ use rustc_span::Span; declare_clippy_lint! { /// ### What it does - /// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but - /// don't because they're a half open range. + /// Checks for ranges which almost include the entire range of letters from 'a' to 'z' + /// or digits from '0' to '9', but don't because they're a half open range. /// /// ### Why is this bad? /// This (`'a'..'z'`) is almost certainly a typo meant to include all letters. @@ -25,21 +25,21 @@ declare_clippy_lint! { /// let _ = 'a'..='z'; /// ``` #[clippy::version = "1.63.0"] - pub ALMOST_COMPLETE_LETTER_RANGE, + pub ALMOST_COMPLETE_RANGE, suspicious, - "almost complete letter range" + "almost complete range" } -impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]); +impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]); -pub struct AlmostCompleteLetterRange { +pub struct AlmostCompleteRange { msrv: Msrv, } -impl AlmostCompleteLetterRange { +impl AlmostCompleteRange { pub fn new(msrv: Msrv) -> Self { Self { msrv } } } -impl EarlyLintPass for AlmostCompleteLetterRange { +impl EarlyLintPass for AlmostCompleteRange { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind { let ctxt = e.span.ctxt(); @@ -87,14 +87,18 @@ fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg Ok(LitKind::Byte(b'A') | LitKind::Char('A')), Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')), ) + | ( + Ok(LitKind::Byte(b'0') | LitKind::Char('0')), + Ok(LitKind::Byte(b'9') | LitKind::Char('9')), + ) ) && !in_external_macro(cx.sess(), span) { span_lint_and_then( cx, - ALMOST_COMPLETE_LETTER_RANGE, + ALMOST_COMPLETE_RANGE, span, - "almost complete ascii letter range", + "almost complete ascii range", |diag| { if let Some((span, sugg)) = sugg { diag.span_suggestion( diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 36daceabe0be..91900542af83 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// ```rust /// let x: Box = Box::default(); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub BOX_DEFAULT, perf, "Using Box::new(T::default()) instead of Box::default()" diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index c6d505c4a181..161e3a698e9e 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -641,7 +641,7 @@ declare_clippy_lint! { /// ```rust,ignore /// let _: = 0_u64; /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.66.0"] pub CAST_NAN_TO_INT, suspicious, "casting a known floating-point NaN into an integer" diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index e4d76f07d6b4..3cd7d1d7e722 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -35,7 +35,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, - crate::almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE_INFO, + crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, crate::approx_const::APPROX_CONSTANT_INFO, crate::as_conversions::AS_CONVERSIONS_INFO, crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO, @@ -525,6 +525,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::returns::NEEDLESS_RETURN_INFO, crate::same_name_method::SAME_NAME_METHOD_INFO, crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO, + crate::semicolon_block::SEMICOLON_INSIDE_BLOCK_INFO, + crate::semicolon_block::SEMICOLON_OUTSIDE_BLOCK_INFO, crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO, crate::serde_api::SERDE_API_MISUSE_INFO, crate::shadow::SHADOW_REUSE_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 31183266acfc..7b43d8ccc67d 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1390,10 +1390,15 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc continue; }, ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), - ty::Alias(ty::Projection, _) if ty.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty), - ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Alias(ty::Opaque, ..) | ty::Placeholder(_) | ty::Dynamic(..) => { - Position::ReborrowStable(precedence).into() + ty::Alias(ty::Projection, _) if ty.has_non_region_param() => { + TyPosition::new_deref_stable_for_result(precedence, ty) }, + ty::Infer(_) + | ty::Error(_) + | ty::Bound(..) + | ty::Alias(ty::Opaque, ..) + | ty::Placeholder(_) + | ty::Dynamic(..) => Position::ReborrowStable(precedence).into(), ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => { Position::ReborrowStable(precedence).into() }, diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 3f0b165f2b60..cf3483d4c00b 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -513,10 +513,7 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain( params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Trait(TraitPredicate { - trait_ref: tcx.mk_trait_ref( - eq_trait_id, - [tcx.mk_param_from_def(param)], - ), + trait_ref: tcx.mk_trait_ref(eq_trait_id, [tcx.mk_param_from_def(param)]), constness: BoundConstness::NotConst, polarity: ImplPolarity::Positive, })))) diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 68122b4cef57..1971cab64ef3 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -47,7 +47,7 @@ declare_clippy_lint! { /// value: usize, /// } /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub DISALLOWED_MACROS, style, "use of a disallowed macro" diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 111b624f5299..69f7c152fc4a 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred}; use clippy_utils::macros::{ - is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage, + is_assert_macro, is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, + FormatParamUsage, }; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; @@ -122,7 +123,7 @@ declare_clippy_lint! { /// /// If a format string contains a numbered argument that cannot be inlined /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub UNINLINED_FORMAT_ARGS, style, "using non-inlined variables in `format!` calls" @@ -290,8 +291,9 @@ fn check_uninlined_args( if args.format_string.span.from_expansion() { return; } - if call_site.edition() < Edition2021 && is_panic(cx, def_id) { - // panic! before 2021 edition considers a single string argument as non-format + if call_site.edition() < Edition2021 && (is_panic(cx, def_id) || is_assert_macro(cx, def_id)) { + // panic!, assert!, and debug_assert! before 2021 edition considers a single string argument as + // non-format return; } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 0634b2798fef..a92f7548ff25 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -10,7 +10,7 @@ use rustc_hir::{ TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::nested_filter::OnlyBodies; +use rustc_middle::{hir::nested_filter::OnlyBodies, ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, sym}; use rustc_span::{Span, Symbol}; @@ -78,6 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) + && !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Alias(ty::Opaque, _)) { span_lint_and_then( cx, diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index bf1351829c6a..6e19343931ec 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -31,7 +31,7 @@ declare_clippy_lint! { /// /// u = u.saturating_add(1); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub IMPLICIT_SATURATING_ADD, style, "Perform saturating addition instead of implicitly checking max bound of data type" diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 4cd7dff4cfd7..eebfb753a0c5 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,13 +1,13 @@ //! lint on indexing and slicing operations use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher; use rustc_ast::ast::RangeLimits; 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_tool_lint, impl_lint_pass}; declare_clippy_lint! { /// ### What it does @@ -82,15 +82,29 @@ declare_clippy_lint! { "indexing/slicing usage" } -declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); +impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); + +#[derive(Copy, Clone)] +pub struct IndexingSlicing { + suppress_restriction_lint_in_const: bool, +} + +impl IndexingSlicing { + pub fn new(suppress_restriction_lint_in_const: bool) -> Self { + Self { + suppress_restriction_lint_in_const, + } + } +} impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + if self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) { return; } if let ExprKind::Index(array, index) = &expr.kind { + let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::Range::hir(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] @@ -141,7 +155,13 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { (None, None) => return, // [..] is ok. }; - span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic", None, help_msg); + span_lint_and_then(cx, INDEXING_SLICING, expr.span, "slicing may panic", |diag| { + diag.help(help_msg); + + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + diag.note(note); + } + }); } else { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind() { @@ -156,14 +176,13 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } } - span_lint_and_help( - cx, - INDEXING_SLICING, - expr.span, - "indexing may panic", - None, - "consider using `.get(n)` or `.get_mut(n)` instead", - ); + span_lint_and_then(cx, INDEXING_SLICING, expr.span, "indexing may panic", |diag| { + diag.help("consider using `.get(n)` or `.get_mut(n)` instead"); + + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + diag.note(note); + } + }); } } } diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 73841f9aa9a2..e88d1764a248 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed}; +use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefIdSet; use rustc_hir::{ def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item, - ItemKind, Mutability, Node, TraitItemRef, TyKind, + ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -16,6 +16,7 @@ use rustc_span::{ source_map::{Span, Spanned, Symbol}, symbol::sym, }; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -428,16 +429,23 @@ fn check_len( fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) { if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { let mut applicability = Applicability::MachineApplicable; + + let lit1 = peel_ref_operators(cx, lit1); + let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability); + + // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will + // cause the code to dereference boolean(won't compile). + if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind { + lit_str = Cow::from(format!("({lit_str})")); + } + span_lint_and_sugg( cx, COMPARISON_TO_EMPTY, span, "comparison to empty slice", &format!("using `{op}is_empty` is clearer and more explicit"), - format!( - "{op}{}.is_empty()", - snippet_with_applicability(cx, lit1.span, "_", &mut applicability) - ), + format!("{op}{lit_str}.is_empty()"), applicability, ); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7b17d8a156d5..39850d598038 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -66,7 +66,7 @@ mod declared_lints; mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` -mod almost_complete_letter_range; +mod almost_complete_range; mod approx_const; mod as_conversions; mod asm_syntax; @@ -256,6 +256,7 @@ mod return_self_not_must_use; mod returns; mod same_name_method; mod self_named_constructors; +mod semicolon_block; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; @@ -507,9 +508,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: } let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone(); + let arithmetic_side_effects_allowed_binary = conf.arithmetic_side_effects_allowed_binary.clone(); + let arithmetic_side_effects_allowed_unary = conf.arithmetic_side_effects_allowed_unary.clone(); store.register_late_pass(move |_| { Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new( - arithmetic_side_effects_allowed.clone(), + arithmetic_side_effects_allowed + .iter() + .flat_map(|el| [[el.clone(), "*".to_string()], ["*".to_string(), el.clone()]]) + .chain(arithmetic_side_effects_allowed_binary.clone()) + .collect(), + arithmetic_side_effects_allowed + .iter() + .chain(arithmetic_side_effects_allowed_unary.iter()) + .cloned() + .collect(), )) }); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); @@ -538,7 +550,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool)); store.register_late_pass(|_| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); - store.register_late_pass(|_| Box::new(misc::MiscLints)); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|_| Box::new(mut_mut::MutMut)); store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); @@ -561,6 +573,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; let allow_expect_in_tests = conf.allow_expect_in_tests; let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; + let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const; store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); store.register_late_pass(move |_| { Box::new(methods::Methods::new( @@ -682,7 +695,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); store.register_late_pass(|_| Box::new(unwrap::Unwrap)); - store.register_late_pass(|_| Box::new(indexing_slicing::IndexingSlicing)); + store.register_late_pass(move |_| { + Box::new(indexing_slicing::IndexingSlicing::new( + suppress_restriction_lint_in_const, + )) + }); store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst)); store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); @@ -859,7 +876,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); - store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv()))); + store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(msrv()))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)); store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)); @@ -884,6 +901,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); + store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index b6f4cf7bbb37..28ee24309cc4 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -25,7 +25,6 @@ pub(super) struct IncrementVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference states: HirIdMap, // incremented variables depth: u32, // depth of conditional expressions - done: bool, } impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { @@ -34,7 +33,6 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { cx, states: HirIdMap::default(), depth: 0, - done: false, } } @@ -51,10 +49,6 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.done { - return; - } - // If node is a variable if let Some(def_id) = path_to_local(expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { @@ -95,7 +89,9 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { walk_expr(self, expr); self.depth -= 1; } else if let ExprKind::Continue(_) = expr.kind { - self.done = true; + // If we see a `continue` block, then we increment depth so that the IncrementVisitor + // state will be set to DontWarn if we see the variable being modified anywhere afterwards. + self.depth += 1; } else { walk_expr(self, expr); } diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index b8ed9b9ec18f..4277455a3a21 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -2,7 +2,7 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{peel_blocks_with_stmt, span_extract_comment, sugg}; +use clippy_utils::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -47,6 +47,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert { if cx.tcx.item_name(macro_call.def_id) == sym::panic; if !cx.tcx.sess.source_map().is_multiline(cond.span); if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); + // Don't change `else if foo { panic!(..) }` to `else { assert!(foo, ..) }` as it just + // shuffles the condition around. + // Should this have a config value? + if !is_else_clause(cx.tcx, expr); then { let mut applicability = Applicability::MachineApplicable; let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 5ab049d8d133..d9ef7dffa020 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,11 +1,12 @@ use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::{diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, source::snippet}; +use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet}; +use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; +use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{def_id::DefId, sym}; +use rustc_span::{def_id::DefId, sym, Span}; declare_clippy_lint! { /// ### What it does @@ -23,6 +24,10 @@ declare_clippy_lint! { /// assert!(matches!(b'X', b'A'..=b'Z')); /// assert!(matches!('2', '0'..='9')); /// assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + /// + /// ('0'..='9').contains(&'0'); + /// ('a'..='z').contains(&'a'); + /// ('A'..='Z').contains(&'A'); /// } /// ``` /// Use instead: @@ -32,6 +37,10 @@ declare_clippy_lint! { /// assert!(b'X'.is_ascii_uppercase()); /// assert!('2'.is_ascii_digit()); /// assert!('x'.is_ascii_alphabetic()); + /// + /// '0'.is_ascii_digit(); + /// 'a'.is_ascii_lowercase(); + /// 'A'.is_ascii_uppercase(); /// } /// ``` #[clippy::version = "1.66.0"] @@ -75,40 +84,21 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { return; } - let Some(macro_call) = root_macro_call(expr.span) else { return }; - - if is_matches_macro(cx, macro_call.def_id) { + if let Some(macro_call) = root_macro_call(expr.span) + && is_matches_macro(cx, macro_call.def_id) { if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { let range = check_pat(&arm.pat.kind); - - if let Some(sugg) = match range { - CharRange::UpperChar => Some("is_ascii_uppercase"), - CharRange::LowerChar => Some("is_ascii_lowercase"), - CharRange::FullChar => Some("is_ascii_alphabetic"), - CharRange::Digit => Some("is_ascii_digit"), - CharRange::Otherwise => None, - } { - let default_snip = ".."; - // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for - // macro span, so we check applicability manually by comparing `recv` is not default. - let recv = snippet(cx, recv.span, default_snip); - - let applicability = if recv == default_snip { - Applicability::HasPlaceholders - } else { - Applicability::MachineApplicable - }; - - span_lint_and_sugg( - cx, - MANUAL_IS_ASCII_CHECK, - macro_call.span, - "manual check for common ascii range", - "try", - format!("{recv}.{sugg}()"), - applicability, - ); - } + check_is_ascii(cx, macro_call.span, recv, &range); + } + } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind + && path.ident.name == sym!(contains) + && let Some(higher::Range { start: Some(start), end: Some(end), limits: RangeLimits::Closed }) + = higher::Range::hir(receiver) { + let range = check_range(start, end); + if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind { + check_is_ascii(cx, expr.span, e, &range); + } else { + check_is_ascii(cx, expr.span, arg, &range); } } } @@ -116,6 +106,37 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { extract_msrv_attr!(LateContext); } +fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &CharRange) { + if let Some(sugg) = match range { + CharRange::UpperChar => Some("is_ascii_uppercase"), + CharRange::LowerChar => Some("is_ascii_lowercase"), + CharRange::FullChar => Some("is_ascii_alphabetic"), + CharRange::Digit => Some("is_ascii_digit"), + CharRange::Otherwise => None, + } { + let default_snip = ".."; + // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for + // macro span, so we check applicability manually by comparing `recv` is not default. + let recv = snippet(cx, recv.span, default_snip); + + let applicability = if recv == default_snip { + Applicability::HasPlaceholders + } else { + Applicability::MachineApplicable + }; + + span_lint_and_sugg( + cx, + MANUAL_IS_ASCII_CHECK, + span, + "manual check for common ascii range", + "try", + format!("{recv}.{sugg}()"), + applicability, + ); + } +} + fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { match pat_kind { PatKind::Or(pats) => { diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 874d36ca9f4e..9c6f8b43c078 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -151,7 +151,12 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: } else { format!("{{ {sn_else} }}") }; - let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};"); + let sn_bl = if matches!(pat.kind, PatKind::Or(..)) { + format!("({sn_pat})") + } else { + sn_pat.into_owned() + }; + let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};"); diag.span_suggestion(span, "consider writing", sugg, app); }, ); diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index c1e6c82487dc..72cdb9c17361 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -70,7 +70,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain { && seg.args.is_none() && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id) - && match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) { + && cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id) + { check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv); diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 429cdc1918d7..06ecbce4e70e 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::peel_mid_ty_refs; +use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; use clippy_utils::{is_diag_item_method, is_diag_trait_item}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -19,6 +19,8 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv let (input_type, ref_count) = peel_mid_ty_refs(input_type); if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())); if return_type == input_type; + if let Some(clone_trait) = cx.tcx.lang_items().clone_trait(); + if implements_trait(cx, return_type, clone_trait, &[]); then { let mut app = Applicability::MachineApplicable; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d2913680cbb7..561e4336593b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3059,7 +3059,7 @@ declare_clippy_lint! { /// let map: HashMap = HashMap::new(); /// let values = map.values().collect::>(); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub ITER_KV_MAP, complexity, "iterating on map using `iter` when `keys` or `values` would do" @@ -3672,7 +3672,10 @@ impl Methods { no_effect_replace::check(cx, expr, arg1, arg2); // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint - if name == "replace" && let Some(("replace", ..)) = method_call(recv) { + if self.msrv.meets(msrvs::PATTERN_TRAIT_CHAR_ARRAY) + && name == "replace" + && let Some(("replace", ..)) = method_call(recv) + { collapsible_str_replace::check(cx, expr, arg1, arg2); } }, diff --git a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs index 7e3bed1e41a9..660b7049cce9 100644 --- a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs +++ b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, match_def_path, paths}; +use clippy_utils::{get_trait_def_id, is_expr_used_or_unified, match_def_path, paths}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -19,6 +19,10 @@ pub(super) fn check<'tcx>( // Get receiver type let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + if is_expr_used_or_unified(cx.tcx, expr) { + return; + } + if let Some(seek_trait_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) && implements_trait(cx, ty, seek_trait_id, &[]) && let ExprKind::Call(func, args1) = arg.kind && diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 516dee20f8b1..9f4beb92b9d2 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -9,12 +9,14 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{ExpnKind, Span}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, is_integer_literal, iter_input_pats, last_path_segment, SpanlessEq}; +use clippy_utils::{ + get_parent_expr, in_constant, is_integer_literal, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq, +}; declare_clippy_lint! { /// ### What it does @@ -120,14 +122,28 @@ declare_clippy_lint! { "using `0 as *{const, mut} T`" } -declare_lint_pass!(MiscLints => [ +pub struct LintPass { + std_or_core: &'static str, +} +impl Default for LintPass { + fn default() -> Self { + Self { std_or_core: "std" } + } +} +impl_lint_pass!(LintPass => [ TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, SHORT_CIRCUIT_STATEMENT, ZERO_PTR, ]); -impl<'tcx> LateLintPass<'tcx> for MiscLints { +impl<'tcx> LateLintPass<'tcx> for LintPass { + fn check_crate(&mut self, cx: &LateContext<'_>) { + if is_no_std_crate(cx) { + self.std_or_core = "core"; + } + } + fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -231,7 +247,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Cast(e, ty) = expr.kind { - check_cast(cx, expr.span, e, ty); + self.check_cast(cx, expr.span, e, ty); return; } if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) { @@ -310,26 +326,28 @@ fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool { } } -fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { - if_chain! { - if let TyKind::Ptr(ref mut_ty) = ty.kind; - if is_integer_literal(e, 0); - if !in_constant(cx, e.hir_id); - then { - let (msg, sugg_fn) = match mut_ty.mutbl { - Mutability::Mut => ("`0 as *mut _` detected", "std::ptr::null_mut"), - Mutability::Not => ("`0 as *const _` detected", "std::ptr::null"), - }; +impl LintPass { + fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { + if_chain! { + if let TyKind::Ptr(ref mut_ty) = ty.kind; + if is_integer_literal(e, 0); + if !in_constant(cx, e.hir_id); + then { + let (msg, sugg_fn) = match mut_ty.mutbl { + Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"), + Mutability::Not => ("`0 as *const _` detected", "ptr::null"), + }; - let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { - (format!("{sugg_fn}()"), Applicability::MachineApplicable) - } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { - (format!("{sugg_fn}::<{mut_ty_snip}>()"), Applicability::MachineApplicable) - } else { - // `MaybeIncorrect` as type inference may not work with the suggested code - (format!("{sugg_fn}()"), Applicability::MaybeIncorrect) - }; - span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); + let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { + (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MachineApplicable) + } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { + (format!("{}::{sugg_fn}::<{mut_ty_snip}>()", self.std_or_core), Applicability::MachineApplicable) + } else { + // `MaybeIncorrect` as type inference may not work with the suggested code + (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MaybeIncorrect) + }; + span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); + } } } } diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 20b82d81a2ae..4fbc8398e373 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -5,25 +5,26 @@ use clippy_utils::{ peel_hir_expr_refs, }; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::{Span, Spanned}; -const HARD_CODED_ALLOWED: &[&str] = &[ - "&str", - "f32", - "f64", - "std::num::Saturating", - "std::num::Wrapping", - "std::string::String", +const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[ + ["f32", "f32"], + ["f64", "f64"], + ["std::num::Saturating", "std::num::Saturating"], + ["std::num::Wrapping", "std::num::Wrapping"], + ["std::string::String", "&str"], ]; +const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; #[derive(Debug)] pub struct ArithmeticSideEffects { - allowed: FxHashSet, + allowed_binary: FxHashMap>, + allowed_unary: FxHashSet, // Used to check whether expressions are constants, such as in enum discriminants and consts const_span: Option, expr_span: Option, @@ -33,19 +34,55 @@ impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]); impl ArithmeticSideEffects { #[must_use] - pub fn new(mut allowed: FxHashSet) -> Self { - allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from)); + pub fn new(user_allowed_binary: Vec<[String; 2]>, user_allowed_unary: Vec) -> Self { + let mut allowed_binary: FxHashMap> = <_>::default(); + for [lhs, rhs] in user_allowed_binary.into_iter().chain( + HARD_CODED_ALLOWED_BINARY + .iter() + .copied() + .map(|[lhs, rhs]| [lhs.to_string(), rhs.to_string()]), + ) { + allowed_binary.entry(lhs).or_default().insert(rhs); + } + let allowed_unary = user_allowed_unary + .into_iter() + .chain(HARD_CODED_ALLOWED_UNARY.iter().copied().map(String::from)) + .collect(); Self { - allowed, + allowed_binary, + allowed_unary, const_span: None, expr_span: None, } } - /// Checks if the given `expr` has any of the inner `allowed` elements. - fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { - self.allowed - .contains(ty.to_string().split('<').next().unwrap_or_default()) + /// Checks if the lhs and the rhs types of a binary operation like "addition" or + /// "multiplication" are present in the inner set of allowed types. + fn has_allowed_binary(&self, lhs_ty: Ty<'_>, rhs_ty: Ty<'_>) -> bool { + let lhs_ty_string = lhs_ty.to_string(); + let lhs_ty_string_elem = lhs_ty_string.split('<').next().unwrap_or_default(); + let rhs_ty_string = rhs_ty.to_string(); + let rhs_ty_string_elem = rhs_ty_string.split('<').next().unwrap_or_default(); + if let Some(rhs_from_specific) = self.allowed_binary.get(lhs_ty_string_elem) + && { + let rhs_has_allowed_ty = rhs_from_specific.contains(rhs_ty_string_elem); + rhs_has_allowed_ty || rhs_from_specific.contains("*") + } + { + true + } else if let Some(rhs_from_glob) = self.allowed_binary.get("*") { + rhs_from_glob.contains(rhs_ty_string_elem) + } else { + false + } + } + + /// Checks if the type of an unary operation like "negation" is present in the inner set of + /// allowed types. + fn has_allowed_unary(&self, ty: Ty<'_>) -> bool { + let ty_string = ty.to_string(); + let ty_string_elem = ty_string.split('<').next().unwrap_or_default(); + self.allowed_unary.contains(ty_string_elem) } // For example, 8i32 or &i64::MAX. @@ -97,8 +134,7 @@ impl ArithmeticSideEffects { }; let lhs_ty = cx.typeck_results().expr_ty(lhs); let rhs_ty = cx.typeck_results().expr_ty(rhs); - let lhs_and_rhs_have_the_same_ty = lhs_ty == rhs_ty; - if lhs_and_rhs_have_the_same_ty && self.is_allowed_ty(lhs_ty) && self.is_allowed_ty(rhs_ty) { + if self.has_allowed_binary(lhs_ty, rhs_ty) { return; } let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { @@ -137,7 +173,7 @@ impl ArithmeticSideEffects { return; } let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - if self.is_allowed_ty(ty) { + if self.has_allowed_unary(ty) { return; } let actual_un_expr = peel_hir_expr_refs(un_expr).0; diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index b48d6c4e2e2a..14a12da862ef 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{clip, unsext}; +use clippy_utils::{clip, peel_hir_expr_refs, unsext}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; use rustc_lint::LateContext; @@ -20,20 +20,76 @@ pub(crate) fn check<'tcx>( if !is_allowed(cx, op, left, right) { match op { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - check_op(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right)); - check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + left, + 0, + expr.span, + peel_hir_expr_refs(right).0.span, + needs_parenthesis(cx, expr, right), + ); + check_op( + cx, + right, + 0, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + right, + 0, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, BinOpKind::Mul => { - check_op(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right)); - check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + left, + 1, + expr.span, + peel_hir_expr_refs(right).0.span, + needs_parenthesis(cx, expr, right), + ); + check_op( + cx, + right, + 1, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, - BinOpKind::Div => check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded), + BinOpKind::Div => check_op( + cx, + right, + 1, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ), BinOpKind::BitAnd => { - check_op(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right)); - check_op(cx, right, -1, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + left, + -1, + expr.span, + peel_hir_expr_refs(right).0.span, + needs_parenthesis(cx, expr, right), + ); + check_op( + cx, + right, + -1, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span), _ => (), diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index b8a20d5ebe9b..eba230da6c39 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -90,9 +90,6 @@ declare_clippy_lint! { /// use rust_decimal::Decimal; /// let _n = Decimal::MAX + Decimal::MAX; /// ``` - /// - /// ### Allowed types - /// Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter. #[clippy::version = "1.64.0"] pub ARITHMETIC_SIDE_EFFECTS, restriction, diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index d612d249c2f0..c2a8db7df038 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -84,7 +84,11 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { if let ItemKind::Use(path, _) = item.kind { - if path.res.iter().all(|res| matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _))) { + if path + .res + .iter() + .all(|res| matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _))) + { return false; } } else if let ItemKind::Macro(..) = item.kind { diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 3aa2490bc44e..41f991a967bf 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -66,7 +66,7 @@ impl RedundantStaticLifetimes { TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime { let snip = snippet(cx, borrow_type.ty.span, ""); - let sugg = format!("&{snip}"); + let sugg = format!("&{}{snip}", borrow_type.mutbl.prefix_str()); span_lint_and_then( cx, REDUNDANT_STATIC_LIFETIMES, diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 8e214218f23a..72c25592609b 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -2,6 +2,7 @@ #[rustfmt::skip] pub static RENAMED_LINTS: &[(&str, &str)] = &[ + ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), ("clippy::blacklisted_name", "clippy::disallowed_names"), ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"), ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"), diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs new file mode 100644 index 000000000000..8f1d1490e1f0 --- /dev/null +++ b/clippy_lints/src/semicolon_block.rs @@ -0,0 +1,137 @@ +use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// + /// Suggests moving the semicolon after a block to the inside of the block, after its last + /// expression. + /// + /// ### Why is this bad? + /// + /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine + /// and this lint suggests inside the block. + /// Take a look at `semicolon_outside_block` for the other alternative. + /// + /// ### Example + /// + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x) }; + /// ``` + /// Use instead: + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x); } + /// ``` + #[clippy::version = "1.66.0"] + pub SEMICOLON_INSIDE_BLOCK, + restriction, + "add a semicolon inside the block" +} +declare_clippy_lint! { + /// ### What it does + /// + /// Suggests moving the semicolon from a block's final expression outside of the block. + /// + /// ### Why is this bad? + /// + /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine + /// and this lint suggests outside the block. + /// Take a look at `semicolon_inside_block` for the other alternative. + /// + /// ### Example + /// + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x); } + /// ``` + /// Use instead: + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x) }; + /// ``` + #[clippy::version = "1.66.0"] + pub SEMICOLON_OUTSIDE_BLOCK, + restriction, + "add a semicolon outside the block" +} +declare_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]); + +impl LateLintPass<'_> for SemicolonBlock { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + match stmt.kind { + StmtKind::Expr(Expr { + kind: ExprKind::Block(block, _), + .. + }) if !block.span.from_expansion() => { + let Block { + expr: None, + stmts: [.., stmt], + .. + } = block else { return }; + let &Stmt { + kind: StmtKind::Semi(expr), + span, + .. + } = stmt else { return }; + semicolon_outside_block(cx, block, expr, span); + }, + StmtKind::Semi(Expr { + kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _), + .. + }) if !block.span.from_expansion() => semicolon_inside_block(cx, block, tail, stmt.span), + _ => (), + } + } +} + +fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) { + let insert_span = tail.span.source_callsite().shrink_to_hi(); + let remove_span = semi_span.with_lo(block.span.hi()); + + span_lint_and_then( + cx, + SEMICOLON_INSIDE_BLOCK, + semi_span, + "consider moving the `;` inside the block for consistent formatting", + |diag| { + multispan_sugg_with_applicability( + diag, + "put the `;` here", + Applicability::MachineApplicable, + [(remove_span, String::new()), (insert_span, ";".to_owned())], + ); + }, + ); +} + +fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>, semi_span: Span) { + let insert_span = block.span.with_lo(block.span.hi()); + // account for macro calls + let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span); + let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi()); + + span_lint_and_then( + cx, + SEMICOLON_OUTSIDE_BLOCK, + block.span, + "consider moving the `;` outside the block for consistent formatting", + |diag| { + multispan_sugg_with_applicability( + diag, + "put the `;` here", + Applicability::MachineApplicable, + [(remove_span, String::new()), (insert_span, ";".to_owned())], + ); + }, + ); +} diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index f4705481d4e6..bc18cad6d381 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{get_expr_use_or_unification_node, peel_blocks, SpanlessEq}; use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths}; -use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -249,6 +249,7 @@ const MAX_LENGTH_BYTE_STRING_LIT: usize = 32; declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]); impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use rustc_ast::LitKind; @@ -316,18 +317,27 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT && !receiver.span.from_expansion() { - span_lint_and_sugg( - cx, - STRING_LIT_AS_BYTES, - e.span, - "calling `as_bytes()` on a string literal", - "consider using a byte string literal instead", - format!( - "b{}", - snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) - ), - applicability, - ); + if let Some((parent, id)) = get_expr_use_or_unification_node(cx.tcx, e) + && let Node::Expr(parent) = parent + && let ExprKind::Match(scrutinee, ..) = parent.kind + && scrutinee.hir_id == id + { + // Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces + // `&[u8]`. This change would prevent matching with different sized slices. + } else { + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `as_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}", + snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) + ), + applicability, + ); + } } } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b6dc8cd7ab11..3e7d0028c0fb 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -205,10 +205,49 @@ macro_rules! define_Conf { } define_Conf! { - /// Lint: Arithmetic. + /// Lint: ARITHMETIC_SIDE_EFFECTS. /// - /// Suppress checking of the passed type names. + /// Suppress checking of the passed type names in all types of operations. + /// + /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed = ["SomeType", "AnotherType"] + /// ``` + /// + /// #### Noteworthy + /// + /// A type, say `SomeType`, listed in this configuration has the same behavior of `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. (arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet = <_>::default()), + /// Lint: ARITHMETIC_SIDE_EFFECTS. + /// + /// Suppress checking of the passed type pair names in binary operations like addition or + /// multiplication. + /// + /// Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless + /// of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`. + /// + /// Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as + /// `["AnotherType", "SomeType"]`. + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] + /// ``` + (arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default()), + /// Lint: ARITHMETIC_SIDE_EFFECTS. + /// + /// Suppress checking of the passed type names in unary operations like "negation" (`-`). + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] + /// ``` + (arithmetic_side_effects_allowed_unary: rustc_data_structures::fx::FxHashSet = <_>::default()), /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX. /// /// Suppress lints whenever the suggested change would cause breakage for other crates. @@ -406,6 +445,14 @@ define_Conf! { /// /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` (allow_mixed_uninlined_format_args: bool = true), + /// Lint: INDEXING_SLICING + /// + /// Whether to suppress a restriction lint in constant code. In same + /// cases the restructured operation might not be unavoidable, as the + /// suggested counterparts are unavailable in constant code. This + /// configuration will cause restriction lints to trigger even + /// if no suggestion can be made. + (suppress_restriction_lint_in_const: bool = false), } /// Search for the configuration file. diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 680935f2329e..9afe02c1e47d 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -7,7 +7,7 @@ use rustc_hir::def::DefKind; use rustc_hir::Item; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, FloatTy}; +use rustc_middle::ty::{self, fast_reject::SimplifiedType, FloatTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; @@ -73,10 +73,10 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { let lang_items = cx.tcx.lang_items(); // This list isn't complete, but good enough for our current list of paths. let incoherent_impls = [ - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), - SimplifiedTypeGen::SliceSimplifiedType, - SimplifiedTypeGen::StrSimplifiedType, + SimplifiedType::FloatSimplifiedType(FloatTy::F32), + SimplifiedType::FloatSimplifiedType(FloatTy::F64), + SimplifiedType::SliceSimplifiedType, + SimplifiedType::StrSimplifiedType, ] .iter() .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied()); diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index fb9f4740ecc5..ac6a566b9cd3 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.67" +version = "0.1.68" edition = "2021" publish = false diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 652f8b4d3c56..43e2d1ec826c 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -196,7 +196,7 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { let parent_id = cx.tcx.hir().get_parent_item(id).def_id; match cx.tcx.hir().get_by_def_id(parent_id) { Node::Item(&Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), + kind: ItemKind::Const(..) | ItemKind::Static(..) | ItemKind::Enum(..), .. }) | Node::TraitItem(&TraitItem { diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index d13b34a66cca..77c5f1155423 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -208,6 +208,12 @@ pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool { ) } +/// Is `def_id` of `assert!` or `debug_assert!` +pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool { + let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false }; + matches!(name, sym::assert_macro | sym::debug_assert_macro) +} + pub enum PanicExpn<'a> { /// No arguments - `panic!()` Empty, diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 12a512f78a69..ba5bc9c3135d 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -21,7 +21,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,65,0 { LET_ELSE } 1,62,0 { BOOL_THEN_SOME } - 1,58,0 { FORMAT_ARGS_CAPTURE } + 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } 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 } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 6417f0f3c713..9ca50105ae57 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -20,7 +20,6 @@ pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", " pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; -pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 8bf542ada04d..e5d7da682813 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -301,10 +301,7 @@ fn check_terminator<'tcx>( check_operand(tcx, value, span, body) }, - TerminatorKind::SwitchInt { - discr, - targets: _, - } => check_operand(tcx, discr, span, body), + TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body), TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())), TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index a6bcb134d408..2773da70d788 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -16,8 +16,8 @@ use rustc_infer::infer::{ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind, - AliasTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, + PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; @@ -30,7 +30,7 @@ use std::iter; use crate::{match_def_path, path_res, paths}; -// Checks if the given type implements copy. +/// Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty.is_copy_modulo_regions(cx.tcx, cx.param_env) } @@ -69,50 +69,66 @@ pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool { /// This method also recurses into opaque type predicates, so call it with `impl Trait` and `U` /// will also return `true`. pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool { - ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => { - if inner_ty == needle { - return true; - } + fn contains_ty_adt_constructor_opaque_inner<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + needle: Ty<'tcx>, + seen: &mut FxHashSet, + ) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => { + if inner_ty == needle { + return true; + } - if inner_ty.ty_adt_def() == needle.ty_adt_def() { - return true; - } + if inner_ty.ty_adt_def() == needle.ty_adt_def() { + return true; + } - if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *inner_ty.kind() { - for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { - match predicate.kind().skip_binder() { - // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through - // and check substituions to find `U`. - ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { - if trait_predicate - .trait_ref - .substs - .types() - .skip(1) // Skip the implicit `Self` generic parameter - .any(|ty| contains_ty_adt_constructor_opaque(cx, ty, needle)) - { - return true; - } - }, - // For `impl Trait`, it will register a predicate of `::Assoc = U`, - // so we check the term for `U`. - ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => { - if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() { - if contains_ty_adt_constructor_opaque(cx, ty, needle) { + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *inner_ty.kind() { + if !seen.insert(def_id) { + return false; + } + + for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { + match predicate.kind().skip_binder() { + // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through + // and check substituions to find `U`. + ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { + if trait_predicate + .trait_ref + .substs + .types() + .skip(1) // Skip the implicit `Self` generic parameter + .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)) + { return true; } - }; - }, - _ => (), + }, + // For `impl Trait`, it will register a predicate of `::Assoc = U`, + // so we check the term for `U`. + ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => { + if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() { + if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) { + return true; + } + }; + }, + _ => (), + } } } - } - false - }, - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }) + false + }, + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) + } + + // A hash set to ensure that the same opaque type (`impl Trait` in RPIT or TAIT) is not + // visited twice. + let mut seen = FxHashSet::default(); + contains_ty_adt_constructor_opaque_inner(cx, ty, needle, &mut seen) } /// Resolves `::Item` for `T` @@ -631,7 +647,9 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)), + ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { + sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)) + }, ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), ty::Dynamic(bounds, _, _) => { let lang_items = cx.tcx.lang_items(); @@ -685,8 +703,7 @@ fn sig_from_bounds<'tcx>( inputs = Some(i); }, PredicateKind::Clause(ty::Clause::Projection(p)) - if Some(p.projection_ty.def_id) == lang_items.fn_once_output() - && p.projection_ty.self_ty() == ty => + if Some(p.projection_ty.def_id) == lang_items.fn_once_output() && p.projection_ty.self_ty() == ty => { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? @@ -1039,10 +1056,7 @@ pub fn make_projection<'tcx>( } } - Some(tcx.mk_alias_ty( - assoc_item.def_id, - substs, - )) + Some(tcx.mk_alias_ty(assoc_item.def_id, substs)) } helper( tcx, diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index 082570f1fe5d..c01e1062cb54 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.67" +version = "0.1.68" edition = "2021" publish = false diff --git a/rust-toolchain b/rust-toolchain index 19fee38db46e..8e21cef32abb 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-12-01" +channel = "nightly-2022-12-17" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/rustc_tools_util/CHANGELOG.md b/rustc_tools_util/CHANGELOG.md new file mode 100644 index 000000000000..1b351da2e7bc --- /dev/null +++ b/rustc_tools_util/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +## Version 0.3.0 + +* Added `setup_version_info!();` macro for automated scripts. +* `get_version_info!()` no longer requires the user to import `rustc_tools_util::VersionInfo` and `std::env` diff --git a/rustc_tools_util/Cargo.toml b/rustc_tools_util/Cargo.toml index 89c3d6aaa89e..877049ae7d0e 100644 --- a/rustc_tools_util/Cargo.toml +++ b/rustc_tools_util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustc_tools_util" -version = "0.2.1" +version = "0.3.0" description = "small helper to generate version information for git packages" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index e947f9c7e66e..eefc661f9635 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -13,43 +13,39 @@ build = "build.rs" List rustc_tools_util as regular AND build dependency. ````toml [dependencies] -rustc_tools_util = "0.2.1" +rustc_tools_util = "0.3.0" [build-dependencies] -rustc_tools_util = "0.2.1" +rustc_tools_util = "0.3.0" ```` In `build.rs`, generate the data in your `main()` -````rust + +```rust fn main() { - println!( - "cargo:rustc-env=GIT_HASH={}", - rustc_tools_util::get_commit_hash().unwrap_or_default() - ); - println!( - "cargo:rustc-env=COMMIT_DATE={}", - rustc_tools_util::get_commit_date().unwrap_or_default() - ); - println!( - "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", - rustc_tools_util::get_channel().unwrap_or_default() - ); + rustc_tools_util::setup_version_info!(); } - -```` +``` Use the version information in your main.rs -````rust -use rustc_tools_util::*; +```rust fn show_version() { let version_info = rustc_tools_util::get_version_info!(); println!("{}", version_info); } -```` +``` + This gives the following output in clippy: -`clippy 0.0.212 (a416c5e 2018-12-14)` +`clippy 0.1.66 (a28f3c8 2022-11-20)` + +## Repository + +This project is part of the rust-lang/rust-clippy repository. The source code +can be found under `./rustc_tools_util/`. +The changelog for `rustc_tools_util` is available under: +[`rustc_tools_util/CHANGELOG.md`](https://github.com/rust-lang/rust-clippy/blob/master/rustc_tools_util/CHANGELOG.md) ## License diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index 01d25c53126f..4c1d8c3733df 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -1,20 +1,20 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] -use std::env; - +/// This macro creates the version string during compilation from the +/// current environment #[macro_export] macro_rules! get_version_info { () => {{ - let major = env!("CARGO_PKG_VERSION_MAJOR").parse::().unwrap(); - let minor = env!("CARGO_PKG_VERSION_MINOR").parse::().unwrap(); - let patch = env!("CARGO_PKG_VERSION_PATCH").parse::().unwrap(); - let crate_name = String::from(env!("CARGO_PKG_NAME")); + let major = std::env!("CARGO_PKG_VERSION_MAJOR").parse::().unwrap(); + let minor = std::env!("CARGO_PKG_VERSION_MINOR").parse::().unwrap(); + let patch = std::env!("CARGO_PKG_VERSION_PATCH").parse::().unwrap(); + let crate_name = String::from(std::env!("CARGO_PKG_NAME")); - let host_compiler = option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string); - let commit_hash = option_env!("GIT_HASH").map(str::to_string); - let commit_date = option_env!("COMMIT_DATE").map(str::to_string); + let host_compiler = std::option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string); + let commit_hash = std::option_env!("GIT_HASH").map(str::to_string); + let commit_date = std::option_env!("COMMIT_DATE").map(str::to_string); - VersionInfo { + $crate::VersionInfo { major, minor, patch, @@ -26,6 +26,24 @@ macro_rules! get_version_info { }}; } +/// This macro can be used in `build.rs` to automatically set the needed +/// environment values, namely `GIT_HASH`, `COMMIT_DATE` and +/// `RUSTC_RELEASE_CHANNEL` +#[macro_export] +macro_rules! setup_version_info { + () => {{ + println!( + "cargo:rustc-env=GIT_HASH={}", + $crate::get_commit_hash().unwrap_or_default() + ); + println!( + "cargo:rustc-env=COMMIT_DATE={}", + $crate::get_commit_date().unwrap_or_default() + ); + println!("cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", $crate::get_channel()); + }}; +} + // some code taken and adapted from RLS and cargo pub struct VersionInfo { pub major: u8, @@ -101,7 +119,7 @@ pub fn get_commit_date() -> Option { #[must_use] pub fn get_channel() -> String { - match env::var("CFG_RELEASE_CHANNEL") { + match std::env::var("CFG_RELEASE_CHANNEL") { Ok(channel) => channel, Err(_) => { // if that failed, try to ask rustc -V, do some parsing and find out @@ -136,8 +154,8 @@ mod test { fn test_struct_local() { let vi = get_version_info!(); assert_eq!(vi.major, 0); - assert_eq!(vi.minor, 2); - assert_eq!(vi.patch, 1); + assert_eq!(vi.minor, 3); + assert_eq!(vi.patch, 0); assert_eq!(vi.crate_name, "rustc_tools_util"); // hard to make positive tests for these since they will always change assert!(vi.commit_hash.is_none()); @@ -147,7 +165,7 @@ mod test { #[test] fn test_display_local() { let vi = get_version_info!(); - assert_eq!(vi.to_string(), "rustc_tools_util 0.2.1"); + assert_eq!(vi.to_string(), "rustc_tools_util 0.3.0"); } #[test] @@ -156,7 +174,7 @@ mod test { let s = format!("{vi:?}"); assert_eq!( s, - "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 1 }" + "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 3, patch: 0 }" ); } } diff --git a/src/driver.rs b/src/driver.rs index 9ec4df8e651b..bcc096c570e1 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -19,7 +19,6 @@ extern crate rustc_span; use rustc_interface::interface; use rustc_session::parse::ParseSess; use rustc_span::symbol::Symbol; -use rustc_tools_util::VersionInfo; use std::borrow::Cow; use std::env; diff --git a/src/main.rs b/src/main.rs index d418d2daa313..7a78b32620d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use rustc_tools_util::VersionInfo; use std::env; use std::path::PathBuf; use std::process::{self, Command}; diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 2a240cc249b0..3ca45404e44b 100644 --- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -7,14 +7,6 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; = help: convert all references to use `sym::Deref` = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` -error: hardcoded path to a diagnostic item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 - | -LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::deref_method` - error: hardcoded path to a language item --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 | @@ -23,5 +15,13 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"] | = help: convert all references to use `LangItem::DerefMut` +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 + | +LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::deref_method` + error: aborting due to 3 previous errors diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs index e8a023ab1764..36db9e54a228 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs @@ -2,32 +2,117 @@ use core::ops::{Add, Neg}; -#[derive(Clone, Copy)] -struct Point { - x: i32, - y: i32, +macro_rules! create { + ($name:ident) => { + #[allow(clippy::arithmetic_side_effects)] + #[derive(Clone, Copy)] + struct $name; + + impl Add<$name> for $name { + type Output = $name; + fn add(self, other: $name) -> Self::Output { + todo!() + } + } + + impl Add for $name { + type Output = $name; + fn add(self, other: i32) -> Self::Output { + todo!() + } + } + + impl Add<$name> for i32 { + type Output = $name; + fn add(self, other: $name) -> Self::Output { + todo!() + } + } + + impl Add for $name { + type Output = $name; + fn add(self, other: i64) -> Self::Output { + todo!() + } + } + + impl Add<$name> for i64 { + type Output = $name; + fn add(self, other: $name) -> Self::Output { + todo!() + } + } + + impl Neg for $name { + type Output = $name; + fn neg(self) -> Self::Output { + todo!() + } + } + }; } -impl Add for Point { - type Output = Self; +create!(Foo); +create!(Bar); +create!(Baz); +create!(OutOfNames); - fn add(self, other: Self) -> Self { - todo!() - } +fn lhs_and_rhs_are_equal() { + // is explicitly on the list + let _ = OutOfNames + OutOfNames; + // is explicitly on the list + let _ = Foo + Foo; + // is implicitly on the list + let _ = Bar + Bar; + // not on the list + let _ = Baz + Baz; } -impl Neg for Point { - type Output = Self; +fn lhs_is_different() { + // is explicitly on the list + let _ = 1i32 + OutOfNames; + // is explicitly on the list + let _ = 1i32 + Foo; + // is implicitly on the list + let _ = 1i32 + Bar; + // not on the list + let _ = 1i32 + Baz; - fn neg(self) -> Self::Output { - todo!() - } + // not on the list + let _ = 1i64 + Foo; + // is implicitly on the list + let _ = 1i64 + Bar; + // not on the list + let _ = 1i64 + Baz; } -fn main() { - let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 }; +fn rhs_is_different() { + // is explicitly on the list + let _ = OutOfNames + 1i32; + // is explicitly on the list + let _ = Foo + 1i32; + // is implicitly on the list + let _ = Bar + 1i32; + // not on the list + let _ = Baz + 1i32; + + // not on the list + let _ = Foo + 1i64; + // is implicitly on the list + let _ = Bar + 1i64; + // not on the list + let _ = Baz + 1i64; +} - let point: Point = Point { x: 1, y: 0 }; - let _ = point + point; - let _ = -point; +fn unary() { + // is explicitly on the list + let _ = -OutOfNames; + // is specifically on the list + let _ = -Foo; + // not on the list + let _ = -Bar; + // not on the list + let _ = -Baz; } + +fn main() {} diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr new file mode 100644 index 000000000000..ad89534aa1b0 --- /dev/null +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr @@ -0,0 +1,58 @@ +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:68:13 + | +LL | let _ = Baz + Baz; + | ^^^^^^^^^ + | + = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:79:13 + | +LL | let _ = 1i32 + Baz; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:82:13 + | +LL | let _ = 1i64 + Foo; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:86:13 + | +LL | let _ = 1i64 + Baz; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:97:13 + | +LL | let _ = Baz + 1i32; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:100:13 + | +LL | let _ = Foo + 1i64; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:104:13 + | +LL | let _ = Baz + 1i64; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:113:13 + | +LL | let _ = -Bar; + | ^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:115:13 + | +LL | let _ = -Baz; + | ^^^^ + +error: aborting due to 9 previous errors + diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml b/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml index e736256f29a4..89cbea7ecfe4 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml +++ b/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml @@ -1 +1,11 @@ -arithmetic-side-effects-allowed = ["Point"] +arithmetic-side-effects-allowed = [ + "OutOfNames" +] +arithmetic-side-effects-allowed-binary = [ + ["Foo", "Foo"], + ["Foo", "i32"], + ["i32", "Foo"], + ["Bar", "*"], + ["*", "Bar"], +] +arithmetic-side-effects-allowed-unary = ["Foo"] diff --git a/tests/ui-toml/suppress_lint_in_const/clippy.toml b/tests/ui-toml/suppress_lint_in_const/clippy.toml new file mode 100644 index 000000000000..1b9384d7e3ee --- /dev/null +++ b/tests/ui-toml/suppress_lint_in_const/clippy.toml @@ -0,0 +1 @@ +suppress-restriction-lint-in-const = true diff --git a/tests/ui-toml/suppress_lint_in_const/test.rs b/tests/ui-toml/suppress_lint_in_const/test.rs new file mode 100644 index 000000000000..5a2df9f6c5d9 --- /dev/null +++ b/tests/ui-toml/suppress_lint_in_const/test.rs @@ -0,0 +1,60 @@ +#![feature(inline_const)] +#![warn(clippy::indexing_slicing)] +// We also check the out_of_bounds_indexing lint here, because it lints similar things and +// we want to avoid false positives. +#![warn(clippy::out_of_bounds_indexing)] +#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] + +const ARR: [i32; 2] = [1, 2]; +const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. +const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + +const fn idx() -> usize { + 1 +} +const fn idx4() -> usize { + 4 +} + +fn main() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + x[index]; + x[4]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + x[1 << 3]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + + x[0]; // Ok, should not produce stderr. + x[3]; // Ok, should not produce stderr. + x[const { idx() }]; // Ok, should not produce stderr. + x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + const { &ARR[idx()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + + let y = &x; + y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 + y[4]; // Ok, rustc will handle references too. + + let v = vec![0; 5]; + v[0]; + v[10]; + v[1 << 3]; + + const N: usize = 15; // Out of bounds + const M: usize = 3; // In bounds + x[N]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + x[M]; // Ok, should not produce stderr. + v[N]; + v[M]; +} + +/// An opaque integer representation +pub struct Integer<'a> { + /// The underlying data + value: &'a [u8], +} +impl<'a> Integer<'a> { + // Check whether `self` holds a negative number or not + pub const fn is_negative(&self) -> bool { + self.value[0] & 0b1000_0000 != 0 + } +} diff --git a/tests/ui-toml/suppress_lint_in_const/test.stderr b/tests/ui-toml/suppress_lint_in_const/test.stderr new file mode 100644 index 000000000000..bc178b7e1319 --- /dev/null +++ b/tests/ui-toml/suppress_lint_in_const/test.stderr @@ -0,0 +1,70 @@ +error[E0080]: evaluation of `main::{constant#3}` failed + --> $DIR/test.rs:31:14 + | +LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +note: erroneous constant used + --> $DIR/test.rs:31:5 + | +LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: indexing may panic + --> $DIR/test.rs:22:5 + | +LL | x[index]; + | ^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + +error: indexing may panic + --> $DIR/test.rs:38:5 + | +LL | v[0]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:39:5 + | +LL | v[10]; + | ^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:40:5 + | +LL | v[1 << 3]; + | ^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:46:5 + | +LL | v[N]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:47:5 + | +LL | v[M]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error[E0080]: evaluation of constant value failed + --> $DIR/test.rs:10:24 + | +LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 01a5e962c949..a22c6a5a0607 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -6,6 +6,8 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie allow-unwrap-in-tests allowed-scripts arithmetic-side-effects-allowed + arithmetic-side-effects-allowed-binary + arithmetic-side-effects-allowed-unary array-size-threshold avoid-breaking-exported-api await-holding-invalid-types @@ -35,6 +37,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie pass-by-value-size-limit single-char-binding-names-threshold standard-macro-braces + suppress-restriction-lint-in-const third-party too-large-for-stack too-many-arguments-threshold diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr deleted file mode 100644 index 9abf6d6c5e7d..000000000000 --- a/tests/ui/almost_complete_letter_range.stderr +++ /dev/null @@ -1,113 +0,0 @@ -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:29:17 - | -LL | let _ = ('a') ..'z'; - | ^^^^^^--^^^ - | | - | help: use an inclusive range: `..=` - | - = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:30:17 - | -LL | let _ = 'A' .. ('Z'); - | ^^^^--^^^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:36:13 - | -LL | let _ = (b'a')..(b'z'); - | ^^^^^^--^^^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:37:13 - | -LL | let _ = b'A'..b'Z'; - | ^^^^--^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:42:13 - | -LL | let _ = a!()..'z'; - | ^^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:45:9 - | -LL | b'a'..b'z' if true => 1, - | ^^^^--^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:46:9 - | -LL | b'A'..b'Z' if true => 2, - | ^^^^--^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:53:9 - | -LL | 'a'..'z' if true => 1, - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:54:9 - | -LL | 'A'..'Z' if true => 2, - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:22:17 - | -LL | let _ = 'a'..'z'; - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` -... -LL | b!(); - | ---- in this macro invocation - | - = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:67:9 - | -LL | 'a'..'z' => 1, - | ^^^--^^^ - | | - | help: use an inclusive range: `...` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:74:13 - | -LL | let _ = 'a'..'z'; - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:76:9 - | -LL | 'a'..'z' => 1, - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: aborting due to 13 previous errors - diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_range.fixed similarity index 56% rename from tests/ui/almost_complete_letter_range.fixed rename to tests/ui/almost_complete_range.fixed index adcbd4d5134d..6046addf7196 100644 --- a/tests/ui/almost_complete_letter_range.fixed +++ b/tests/ui/almost_complete_range.fixed @@ -4,9 +4,10 @@ #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] -#![warn(clippy::almost_complete_letter_range)] +#![warn(clippy::almost_complete_range)] #![allow(ellipsis_inclusive_range_patterns)] #![allow(clippy::needless_parens_on_range_literals)] +#![allow(clippy::double_parens)] #[macro_use] extern crate macro_rules; @@ -16,10 +17,22 @@ macro_rules! a { 'a' }; } +macro_rules! A { + () => { + 'A' + }; +} +macro_rules! zero { + () => { + '0' + }; +} macro_rules! b { () => { let _ = 'a'..='z'; + let _ = 'A'..='Z'; + let _ = '0'..='9'; }; } @@ -28,36 +41,46 @@ fn main() { { let _ = ('a') ..='z'; let _ = 'A' ..= ('Z'); + let _ = ((('0'))) ..= ('9'); } let _ = 'b'..'z'; let _ = 'B'..'Z'; + let _ = '1'..'9'; let _ = (b'a')..=(b'z'); let _ = b'A'..=b'Z'; + let _ = b'0'..=b'9'; let _ = b'b'..b'z'; let _ = b'B'..b'Z'; + let _ = b'1'..b'9'; let _ = a!()..='z'; + let _ = A!()..='Z'; + let _ = zero!()..='9'; let _ = match 0u8 { b'a'..=b'z' if true => 1, b'A'..=b'Z' if true => 2, - b'b'..b'z' => 3, - b'B'..b'Z' => 4, - _ => 5, + b'0'..=b'9' if true => 3, + b'b'..b'z' => 4, + b'B'..b'Z' => 5, + b'1'..b'9' => 6, + _ => 7, }; let _ = match 'x' { 'a'..='z' if true => 1, 'A'..='Z' if true => 2, - 'b'..'z' => 3, - 'B'..'Z' => 4, - _ => 5, + '0'..='9' if true => 3, + 'b'..'z' => 4, + 'B'..'Z' => 5, + '1'..'9' => 6, + _ => 7, }; - almost_complete_letter_range!(); + almost_complete_range!(); b!(); } @@ -65,15 +88,21 @@ fn main() { fn _under_msrv() { let _ = match 'a' { 'a'...'z' => 1, - _ => 2, + 'A'...'Z' => 2, + '0'...'9' => 3, + _ => 4, }; } #[clippy::msrv = "1.26"] fn _meets_msrv() { let _ = 'a'..='z'; + let _ = 'A'..='Z'; + let _ = '0'..='9'; let _ = match 'a' { 'a'..='z' => 1, - _ => 2, + 'A'..='Z' => 1, + '0'..='9' => 3, + _ => 4, }; } diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_range.rs similarity index 57% rename from tests/ui/almost_complete_letter_range.rs rename to tests/ui/almost_complete_range.rs index 9979316eca42..ae7e07ab872b 100644 --- a/tests/ui/almost_complete_letter_range.rs +++ b/tests/ui/almost_complete_range.rs @@ -4,9 +4,10 @@ #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] -#![warn(clippy::almost_complete_letter_range)] +#![warn(clippy::almost_complete_range)] #![allow(ellipsis_inclusive_range_patterns)] #![allow(clippy::needless_parens_on_range_literals)] +#![allow(clippy::double_parens)] #[macro_use] extern crate macro_rules; @@ -16,10 +17,22 @@ macro_rules! a { 'a' }; } +macro_rules! A { + () => { + 'A' + }; +} +macro_rules! zero { + () => { + '0' + }; +} macro_rules! b { () => { let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; }; } @@ -28,36 +41,46 @@ fn main() { { let _ = ('a') ..'z'; let _ = 'A' .. ('Z'); + let _ = ((('0'))) .. ('9'); } let _ = 'b'..'z'; let _ = 'B'..'Z'; + let _ = '1'..'9'; let _ = (b'a')..(b'z'); let _ = b'A'..b'Z'; + let _ = b'0'..b'9'; let _ = b'b'..b'z'; let _ = b'B'..b'Z'; + let _ = b'1'..b'9'; let _ = a!()..'z'; + let _ = A!()..'Z'; + let _ = zero!()..'9'; let _ = match 0u8 { b'a'..b'z' if true => 1, b'A'..b'Z' if true => 2, - b'b'..b'z' => 3, - b'B'..b'Z' => 4, - _ => 5, + b'0'..b'9' if true => 3, + b'b'..b'z' => 4, + b'B'..b'Z' => 5, + b'1'..b'9' => 6, + _ => 7, }; let _ = match 'x' { 'a'..'z' if true => 1, 'A'..'Z' if true => 2, - 'b'..'z' => 3, - 'B'..'Z' => 4, - _ => 5, + '0'..'9' if true => 3, + 'b'..'z' => 4, + 'B'..'Z' => 5, + '1'..'9' => 6, + _ => 7, }; - almost_complete_letter_range!(); + almost_complete_range!(); b!(); } @@ -65,15 +88,21 @@ fn main() { fn _under_msrv() { let _ = match 'a' { 'a'..'z' => 1, - _ => 2, + 'A'..'Z' => 2, + '0'..'9' => 3, + _ => 4, }; } #[clippy::msrv = "1.26"] fn _meets_msrv() { let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; let _ = match 'a' { 'a'..'z' => 1, - _ => 2, + 'A'..'Z' => 1, + '0'..'9' => 3, + _ => 4, }; } diff --git a/tests/ui/almost_complete_range.stderr b/tests/ui/almost_complete_range.stderr new file mode 100644 index 000000000000..a7a532878502 --- /dev/null +++ b/tests/ui/almost_complete_range.stderr @@ -0,0 +1,235 @@ +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:42:17 + | +LL | let _ = ('a') ..'z'; + | ^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` + | + = note: `-D clippy::almost-complete-range` implied by `-D warnings` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:43:17 + | +LL | let _ = 'A' .. ('Z'); + | ^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:44:17 + | +LL | let _ = ((('0'))) .. ('9'); + | ^^^^^^^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:51:13 + | +LL | let _ = (b'a')..(b'z'); + | ^^^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:52:13 + | +LL | let _ = b'A'..b'Z'; + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:53:13 + | +LL | let _ = b'0'..b'9'; + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:59:13 + | +LL | let _ = a!()..'z'; + | ^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:60:13 + | +LL | let _ = A!()..'Z'; + | ^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:61:13 + | +LL | let _ = zero!()..'9'; + | ^^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:64:9 + | +LL | b'a'..b'z' if true => 1, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:65:9 + | +LL | b'A'..b'Z' if true => 2, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:66:9 + | +LL | b'0'..b'9' if true => 3, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:74:9 + | +LL | 'a'..'z' if true => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:75:9 + | +LL | 'A'..'Z' if true => 2, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:76:9 + | +LL | '0'..'9' if true => 3, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:33:17 + | +LL | let _ = 'a'..'z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:34:17 + | +LL | let _ = 'A'..'Z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:35:17 + | +LL | let _ = '0'..'9'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:90:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:91:9 + | +LL | 'A'..'Z' => 2, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:92:9 + | +LL | '0'..'9' => 3, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:99:13 + | +LL | let _ = 'a'..'z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:100:13 + | +LL | let _ = 'A'..'Z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:101:13 + | +LL | let _ = '0'..'9'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:103:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:104:9 + | +LL | 'A'..'Z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:105:9 + | +LL | '0'..'9' => 3, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: aborting due to 27 previous errors + diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 0259a0824e79..9fe4b7cf28d8 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,28 +1,10 @@ -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:78:13 - | -LL | let _ = String::new() + ""; - | ^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:86:27 - | -LL | let inferred_string = string + ""; - | ^^^^^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:90:13 - | -LL | let _ = inferred_string + ""; - | ^^^^^^^^^^^^^^^^^^^^ - error: arithmetic operation that can potentially result in unexpected side-effects --> $DIR/arithmetic_side_effects.rs:165:5 | LL | _n += 1; | ^^^^^^^ + | + = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects --> $DIR/arithmetic_side_effects.rs:166:5 @@ -348,5 +330,5 @@ error: arithmetic operation that can potentially result in unexpected side-effec LL | _n = -&_n; | ^^^^ -error: aborting due to 58 previous errors +error: aborting due to 55 previous errors diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index ef3ca9aea380..1e5f20e8c39b 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -142,8 +142,10 @@ macro_rules! equatable_if_let { } #[macro_export] -macro_rules! almost_complete_letter_range { +macro_rules! almost_complete_range { () => { let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; }; } diff --git a/tests/ui/cast_lossless_integer.fixed b/tests/ui/cast_lossless_integer.fixed index 72a708b40737..925cbf25368f 100644 --- a/tests/ui/cast_lossless_integer.fixed +++ b/tests/ui/cast_lossless_integer.fixed @@ -45,3 +45,9 @@ mod cast_lossless_in_impl { } } } + +#[derive(PartialEq, Debug)] +#[repr(i64)] +enum Test { + A = u32::MAX as i64 + 1, +} diff --git a/tests/ui/cast_lossless_integer.rs b/tests/ui/cast_lossless_integer.rs index 34bb47181e69..c82bd9108d23 100644 --- a/tests/ui/cast_lossless_integer.rs +++ b/tests/ui/cast_lossless_integer.rs @@ -45,3 +45,9 @@ mod cast_lossless_in_impl { } } } + +#[derive(PartialEq, Debug)] +#[repr(i64)] +enum Test { + A = u32::MAX as i64 + 1, +} diff --git a/tests/ui/collapsible_str_replace.fixed b/tests/ui/collapsible_str_replace.fixed index 49fc9a9629e2..9792ae9ed6b8 100644 --- a/tests/ui/collapsible_str_replace.fixed +++ b/tests/ui/collapsible_str_replace.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![allow(unused)] #![warn(clippy::collapsible_str_replace)] fn get_filter() -> char { @@ -71,3 +72,13 @@ fn main() { .replace('u', iter.next().unwrap()) .replace('s', iter.next().unwrap()); } + +#[clippy::msrv = "1.57"] +fn msrv_1_57() { + let _ = "".replace('a', "1.57").replace('b', "1.57"); +} + +#[clippy::msrv = "1.58"] +fn msrv_1_58() { + let _ = "".replace(['a', 'b'], "1.58"); +} diff --git a/tests/ui/collapsible_str_replace.rs b/tests/ui/collapsible_str_replace.rs index e3e25c4146ff..baee185b79ea 100644 --- a/tests/ui/collapsible_str_replace.rs +++ b/tests/ui/collapsible_str_replace.rs @@ -1,5 +1,6 @@ // run-rustfix +#![allow(unused)] #![warn(clippy::collapsible_str_replace)] fn get_filter() -> char { @@ -74,3 +75,13 @@ fn main() { .replace('u', iter.next().unwrap()) .replace('s', iter.next().unwrap()); } + +#[clippy::msrv = "1.57"] +fn msrv_1_57() { + let _ = "".replace('a', "1.57").replace('b', "1.57"); +} + +#[clippy::msrv = "1.58"] +fn msrv_1_58() { + let _ = "".replace('a', "1.58").replace('b', "1.58"); +} diff --git a/tests/ui/collapsible_str_replace.stderr b/tests/ui/collapsible_str_replace.stderr index 8e3daf3b898a..223358cf53f3 100644 --- a/tests/ui/collapsible_str_replace.stderr +++ b/tests/ui/collapsible_str_replace.stderr @@ -1,5 +1,5 @@ error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:19:27 + --> $DIR/collapsible_str_replace.rs:20:27 | LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")` @@ -7,19 +7,19 @@ LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l"); = note: `-D clippy::collapsible-str-replace` implied by `-D warnings` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:21:27 + --> $DIR/collapsible_str_replace.rs:22:27 | LL | let _ = "hesuo worpd".replace('s', l).replace('u', l); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], l)` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:23:27 + --> $DIR/collapsible_str_replace.rs:24:27 | LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:26:10 + --> $DIR/collapsible_str_replace.rs:27:10 | LL | .replace('s', "l") | __________^ @@ -29,58 +29,64 @@ LL | | .replace('d', "l"); | |__________________________^ help: replace with: `replace(['s', 'u', 'p', 'd'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:31:27 + --> $DIR/collapsible_str_replace.rs:32:27 | LL | let _ = "hesuo world".replace(s, "l").replace('u', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:33:27 + --> $DIR/collapsible_str_replace.rs:34:27 | LL | let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:35:27 + --> $DIR/collapsible_str_replace.rs:36:27 | LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:37:27 + --> $DIR/collapsible_str_replace.rs:38:27 | LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, p], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:39:27 + --> $DIR/collapsible_str_replace.rs:40:27 | LL | let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:41:45 + --> $DIR/collapsible_str_replace.rs:42:45 | LL | let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:44:47 + --> $DIR/collapsible_str_replace.rs:45:47 | LL | let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['d', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:46:28 + --> $DIR/collapsible_str_replace.rs:47:28 | LL | let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([d, 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:48:27 + --> $DIR/collapsible_str_replace.rs:49:27 | LL | let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([get_filter(), 's'], "l")` -error: aborting due to 13 previous errors +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:86:16 + | +LL | let _ = "".replace('a', "1.58").replace('b', "1.58"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['a', 'b'], "1.58")` + +error: aborting due to 14 previous errors diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index 6eddc01e2c47..46565a97f005 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -189,3 +189,33 @@ mod issue_7920 { } } } + +mod issue_10058 { + pub fn test() { + // should not lint since we are increasing counter potentially more than once in the loop + let values = [0, 1, 0, 1, 1, 1, 0, 1, 0, 1]; + let mut counter = 0; + for value in values { + counter += 1; + + if value == 0 { + continue; + } + + counter += 1; + } + } + + pub fn test2() { + // should not lint since we are increasing counter potentially more than once in the loop + let values = [0, 1, 0, 1, 1, 1, 0, 1, 0, 1]; + let mut counter = 0; + for value in values { + counter += 1; + + if value != 0 { + counter += 1; + } + } + } +} diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed index 125c9a69cd3f..72d635c2ccd6 100644 --- a/tests/ui/from_over_into.fixed +++ b/tests/ui/from_over_into.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(type_alias_impl_trait)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -81,4 +82,10 @@ fn msrv_1_41() { } } +type Opaque = impl Sized; +struct IntoOpaque; +impl Into for IntoOpaque { + fn into(self) -> Opaque {} +} + fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 5aa127bfabe4..965f4d5d7859 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(type_alias_impl_trait)] #![warn(clippy::from_over_into)] #![allow(unused)] @@ -81,4 +82,10 @@ fn msrv_1_41() { } } +type Opaque = impl Sized; +struct IntoOpaque; +impl Into for IntoOpaque { + fn into(self) -> Opaque {} +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index a1764a5ea12a..3c4d011d6fb4 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,5 +1,5 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:9:1 + --> $DIR/from_over_into.rs:10:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL ~ StringWrapper(val) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:17:1 + --> $DIR/from_over_into.rs:18:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL ~ SelfType(String::new()) | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:32:1 + --> $DIR/from_over_into.rs:33:1 | LL | impl Into for X { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL ~ let _: X = val; | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:44:1 + --> $DIR/from_over_into.rs:45:1 | LL | impl core::convert::Into for crate::ExplicitPaths { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ LL ~ val.0 | error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:77:5 + --> $DIR/from_over_into.rs:78:5 | LL | impl Into> for Vec { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed index e7b9a78c5dbc..cac69ef42c41 100644 --- a/tests/ui/identity_op.fixed +++ b/tests/ui/identity_op.fixed @@ -65,7 +65,7 @@ fn main() { 42; 1; 42; - &x; + x; x; let mut a = A(String::new()); @@ -112,6 +112,10 @@ fn main() { 2 * { a }; (({ a } + 4)); 1; + + // Issue #9904 + let x = 0i32; + let _: i32 = x; } pub fn decide(a: bool, b: bool) -> u32 { diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 9a435cdbb753..33201aad4f64 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -112,6 +112,10 @@ fn main() { 2 * (0 + { a }); 1 * ({ a } + 4); 1 * 1; + + // Issue #9904 + let x = 0i32; + let _: i32 = &x + 0; } pub fn decide(a: bool, b: bool) -> u32 { diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 1a104a20b841..3ba557d18b24 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -70,7 +70,7 @@ error: this operation has no effect --> $DIR/identity_op.rs:68:5 | LL | &x >> 0; - | ^^^^^^^ help: consider reducing it to: `&x` + | ^^^^^^^ help: consider reducing it to: `x` error: this operation has no effect --> $DIR/identity_op.rs:69:5 @@ -229,10 +229,16 @@ LL | 1 * 1; | ^^^^^ help: consider reducing it to: `1` error: this operation has no effect - --> $DIR/identity_op.rs:118:5 + --> $DIR/identity_op.rs:118:18 + | +LL | let _: i32 = &x + 0; + | ^^^^^^ help: consider reducing it to: `x` + +error: this operation has no effect + --> $DIR/identity_op.rs:122:5 | LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })` -error: aborting due to 39 previous errors +error: aborting due to 40 previous errors diff --git a/tests/ui/implicit_clone.fixed b/tests/ui/implicit_clone.fixed index 33770fc2a2cf..51b1afbe5ac8 100644 --- a/tests/ui/implicit_clone.fixed +++ b/tests/ui/implicit_clone.fixed @@ -115,4 +115,14 @@ fn main() { let pathbuf_ref = &pathbuf_ref; let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf` let _ = (**pathbuf_ref).clone(); + + struct NoClone; + impl ToOwned for NoClone { + type Owned = Self; + fn to_owned(&self) -> Self { + NoClone + } + } + let no_clone = &NoClone; + let _ = no_clone.to_owned(); } diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index fc896525bd27..8a9027433d95 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -115,4 +115,14 @@ fn main() { let pathbuf_ref = &pathbuf_ref; let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf` let _ = pathbuf_ref.to_path_buf(); + + struct NoClone; + impl ToOwned for NoClone { + type Owned = Self; + fn to_owned(&self) -> Self { + NoClone + } + } + let no_clone = &NoClone; + let _ = no_clone.to_owned(); } diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 4476e0eb9220..26abc9edb5e4 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -6,7 +6,7 @@ #![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] const ARR: [i32; 2] = [1, 2]; -const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. +const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. const fn idx() -> usize { @@ -27,8 +27,8 @@ fn main() { x[3]; // Ok, should not produce stderr. x[const { idx() }]; // Ok, should not produce stderr. x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. - const { &ARR[idx()] }; // Ok, should not produce stderr. - const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. + const { &ARR[idx()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. + const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. let y = &x; y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index d8b6e3f1262b..8fd77913a3fd 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,13 +1,32 @@ +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:9:20 + | +LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. + | ^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:10:24 + | +LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks + error[E0080]: evaluation of `main::{constant#3}` failed --> $DIR/indexing_slicing_index.rs:31:14 | -LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. +LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 note: erroneous constant used --> $DIR/indexing_slicing_index.rs:31:5 | -LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. +LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^^^^^^^^^^^^^ error: indexing may panic @@ -17,7 +36,24 @@ LL | x[index]; | ^^^^^^^^ | = help: consider using `.get(n)` or `.get_mut(n)` instead - = note: `-D clippy::indexing-slicing` implied by `-D warnings` + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:30:14 + | +LL | const { &ARR[idx()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. + | ^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:31:14 + | +LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. + | ^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks error: indexing may panic --> $DIR/indexing_slicing_index.rs:38:5 @@ -65,6 +101,6 @@ error[E0080]: evaluation of constant value failed LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -error: aborting due to 8 previous errors +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index 1f3b8ac99b19..c1c0b5ae40f6 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -3,6 +3,9 @@ #![warn(clippy::len_zero)] #![allow(dead_code, unused, clippy::len_without_is_empty)] +extern crate core; +use core::ops::Deref; + pub struct One; struct Wither; @@ -56,6 +59,26 @@ impl WithIsEmpty for Wither { } } +struct DerefToDerefToString; + +impl Deref for DerefToDerefToString { + type Target = DerefToString; + + fn deref(&self) -> &Self::Target { + &DerefToString {} + } +} + +struct DerefToString; + +impl Deref for DerefToString { + type Target = str; + + fn deref(&self) -> &Self::Target { + "Hello, world!" + } +} + fn main() { let x = [1, 2]; if x.is_empty() { @@ -64,6 +87,23 @@ fn main() { if "".is_empty() {} + let s = "Hello, world!"; + let s1 = &s; + let s2 = &s1; + let s3 = &s2; + let s4 = &s3; + let s5 = &s4; + let s6 = &s5; + println!("{}", s1.is_empty()); + println!("{}", s2.is_empty()); + println!("{}", s3.is_empty()); + println!("{}", s4.is_empty()); + println!("{}", s5.is_empty()); + println!("{}", (s6).is_empty()); + + let d2s = DerefToDerefToString {}; + println!("{}", (**d2s).is_empty()); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index dc21de0001b6..cc2eb05b6bfd 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -3,6 +3,9 @@ #![warn(clippy::len_zero)] #![allow(dead_code, unused, clippy::len_without_is_empty)] +extern crate core; +use core::ops::Deref; + pub struct One; struct Wither; @@ -56,6 +59,26 @@ impl WithIsEmpty for Wither { } } +struct DerefToDerefToString; + +impl Deref for DerefToDerefToString { + type Target = DerefToString; + + fn deref(&self) -> &Self::Target { + &DerefToString {} + } +} + +struct DerefToString; + +impl Deref for DerefToString { + type Target = str; + + fn deref(&self) -> &Self::Target { + "Hello, world!" + } +} + fn main() { let x = [1, 2]; if x.len() == 0 { @@ -64,6 +87,23 @@ fn main() { if "".len() == 0 {} + let s = "Hello, world!"; + let s1 = &s; + let s2 = &s1; + let s3 = &s2; + let s4 = &s3; + let s5 = &s4; + let s6 = &s5; + println!("{}", *s1 == ""); + println!("{}", **s2 == ""); + println!("{}", ***s3 == ""); + println!("{}", ****s4 == ""); + println!("{}", *****s5 == ""); + println!("{}", ******(s6) == ""); + + let d2s = DerefToDerefToString {}; + println!("{}", &**d2s == ""); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. diff --git a/tests/ui/len_zero.stderr b/tests/ui/len_zero.stderr index 6c71f1beeac6..b6f13780253c 100644 --- a/tests/ui/len_zero.stderr +++ b/tests/ui/len_zero.stderr @@ -1,5 +1,5 @@ error: length comparison to zero - --> $DIR/len_zero.rs:61:8 + --> $DIR/len_zero.rs:84:8 | LL | if x.len() == 0 { | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()` @@ -7,82 +7,126 @@ LL | if x.len() == 0 { = note: `-D clippy::len-zero` implied by `-D warnings` error: length comparison to zero - --> $DIR/len_zero.rs:65:8 + --> $DIR/len_zero.rs:88:8 | LL | if "".len() == 0 {} | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()` +error: comparison to empty slice + --> $DIR/len_zero.rs:97:20 + | +LL | println!("{}", *s1 == ""); + | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s1.is_empty()` + | + = note: `-D clippy::comparison-to-empty` implied by `-D warnings` + +error: comparison to empty slice + --> $DIR/len_zero.rs:98:20 + | +LL | println!("{}", **s2 == ""); + | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s2.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:99:20 + | +LL | println!("{}", ***s3 == ""); + | ^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s3.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:100:20 + | +LL | println!("{}", ****s4 == ""); + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s4.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:101:20 + | +LL | println!("{}", *****s5 == ""); + | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s5.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:102:20 + | +LL | println!("{}", ******(s6) == ""); + | ^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(s6).is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:105:20 + | +LL | println!("{}", &**d2s == ""); + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()` + error: length comparison to zero - --> $DIR/len_zero.rs:80:8 + --> $DIR/len_zero.rs:120:8 | LL | if has_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:83:8 + --> $DIR/len_zero.rs:123:8 | LL | if has_is_empty.len() != 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:86:8 + --> $DIR/len_zero.rs:126:8 | LL | if has_is_empty.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:89:8 + --> $DIR/len_zero.rs:129:8 | LL | if has_is_empty.len() < 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:92:8 + --> $DIR/len_zero.rs:132:8 | LL | if has_is_empty.len() >= 1 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:103:8 + --> $DIR/len_zero.rs:143:8 | LL | if 0 == has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:106:8 + --> $DIR/len_zero.rs:146:8 | LL | if 0 != has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:109:8 + --> $DIR/len_zero.rs:149:8 | LL | if 0 < has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:112:8 + --> $DIR/len_zero.rs:152:8 | LL | if 1 <= has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:115:8 + --> $DIR/len_zero.rs:155:8 | LL | if 1 > has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:129:8 + --> $DIR/len_zero.rs:169:8 | LL | if with_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:142:8 + --> $DIR/len_zero.rs:182:8 | LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` -error: aborting due to 14 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index c9a819ba5354..638320dd6eec 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -62,6 +62,11 @@ fn main() { panic!("panic5"); } assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } } fn issue7730(a: u8) { diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed index 2f62de51cadc..8c7e919bf62a 100644 --- a/tests/ui/manual_assert.edition2021.fixed +++ b/tests/ui/manual_assert.edition2021.fixed @@ -50,6 +50,11 @@ fn main() { assert!(!(b.is_empty() || a.is_empty()), "panic4"); assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } } fn issue7730(a: u8) { diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 237638ee1344..3555ac29243a 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -65,7 +65,7 @@ LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:73:5 + --> $DIR/manual_assert.rs:78:5 | LL | / if a > 2 { LL | | // comment diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index 6a4cc2468d41..f037c5b8405c 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -66,6 +66,11 @@ fn main() { if a.is_empty() { panic!("with expansion {}", one!()) } + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } } fn issue7730(a: u8) { diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index 231ba83b1426..5b2b44c2fdb2 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -15,6 +15,19 @@ fn main() { assert!('x'.is_ascii_alphabetic()); assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); + + b'0'.is_ascii_digit(); + b'a'.is_ascii_lowercase(); + b'A'.is_ascii_uppercase(); + + '0'.is_ascii_digit(); + 'a'.is_ascii_lowercase(); + 'A'.is_ascii_uppercase(); + + let cool_letter = &'g'; + cool_letter.is_ascii_digit(); + cool_letter.is_ascii_lowercase(); + cool_letter.is_ascii_uppercase(); } #[clippy::msrv = "1.23"] diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index 39ee6151c56f..c9433f33a1b6 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -15,6 +15,19 @@ fn main() { assert!(matches!('x', 'A'..='Z' | 'a'..='z')); assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); + + (b'0'..=b'9').contains(&b'0'); + (b'a'..=b'z').contains(&b'a'); + (b'A'..=b'Z').contains(&b'A'); + + ('0'..='9').contains(&'0'); + ('a'..='z').contains(&'a'); + ('A'..='Z').contains(&'A'); + + let cool_letter = &'g'; + ('0'..='9').contains(cool_letter); + ('a'..='z').contains(cool_letter); + ('A'..='Z').contains(cool_letter); } #[clippy::msrv = "1.23"] diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index 397cbe05c822..ee60188506d6 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -43,28 +43,82 @@ LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:29:13 + --> $DIR/manual_is_ascii_check.rs:19:5 + | +LL | (b'0'..=b'9').contains(&b'0'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'0'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:20:5 + | +LL | (b'a'..=b'z').contains(&b'a'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'a'.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:21:5 + | +LL | (b'A'..=b'Z').contains(&b'A'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'A'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:23:5 + | +LL | ('0'..='9').contains(&'0'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'0'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:24:5 + | +LL | ('a'..='z').contains(&'a'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'a'.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:25:5 + | +LL | ('A'..='Z').contains(&'A'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'A'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:28:5 + | +LL | ('0'..='9').contains(cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:29:5 + | +LL | ('a'..='z').contains(cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:30:5 + | +LL | ('A'..='Z').contains(cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:42:13 | LL | assert!(matches!(b'1', b'0'..=b'9')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:30:13 + --> $DIR/manual_is_ascii_check.rs:43:13 | LL | assert!(matches!('X', 'A'..='Z')); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:31:13 + --> $DIR/manual_is_ascii_check.rs:44:13 | LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` error: manual check for common ascii range - --> $DIR/manual_is_ascii_check.rs:41:23 + --> $DIR/manual_is_ascii_check.rs:54:23 | LL | const FOO: bool = matches!('x', '0'..='9'); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` -error: aborting due to 11 previous errors +error: aborting due to 20 previous errors diff --git a/tests/ui/manual_let_else_match.rs b/tests/ui/manual_let_else_match.rs index 93c86ca24fea..28caed9d79df 100644 --- a/tests/ui/manual_let_else_match.rs +++ b/tests/ui/manual_let_else_match.rs @@ -64,6 +64,13 @@ fn fire() { Ok(v) => v, Err(()) => return, }; + + let f = Variant::Bar(1); + + let _value = match f { + Variant::Bar(_) | Variant::Baz(_) => (), + _ => return, + }; } fn not_fire() { diff --git a/tests/ui/manual_let_else_match.stderr b/tests/ui/manual_let_else_match.stderr index 38be5ac54547..cd5e9a9ac39c 100644 --- a/tests/ui/manual_let_else_match.stderr +++ b/tests/ui/manual_let_else_match.stderr @@ -25,7 +25,7 @@ LL | / let v = match h() { LL | | (Some(_), Some(_)) | (None, None) => continue, LL | | (Some(v), None) | (None, Some(v)) => v, LL | | }; - | |__________^ help: consider writing: `let (Some(v), None) | (None, Some(v)) = h() else { continue };` + | |__________^ help: consider writing: `let ((Some(v), None) | (None, Some(v))) = h() else { continue };` error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:49:9 @@ -34,7 +34,7 @@ LL | / let v = match build_enum() { LL | | _ => continue, LL | | Variant::Bar(v) | Variant::Baz(v) => v, LL | | }; - | |__________^ help: consider writing: `let Variant::Bar(v) | Variant::Baz(v) = build_enum() else { continue };` + | |__________^ help: consider writing: `let (Variant::Bar(v) | Variant::Baz(v)) = build_enum() else { continue };` error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:57:5 @@ -54,5 +54,14 @@ LL | | Err(()) => return, LL | | }; | |______^ help: consider writing: `let Ok(v) = f().map_err(|_| ()) else { return };` -error: aborting due to 6 previous errors +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:70:5 + | +LL | / let _value = match f { +LL | | Variant::Bar(_) | Variant::Baz(_) => (), +LL | | _ => return, +LL | | }; + | |______^ help: consider writing: `let (Variant::Bar(_) | Variant::Baz(_)) = f else { return };` + +error: aborting due to 7 previous errors diff --git a/tests/ui/needless_parens_on_range_literals.fixed b/tests/ui/needless_parens_on_range_literals.fixed index 1bd75c806bc9..f11330a8916d 100644 --- a/tests/ui/needless_parens_on_range_literals.fixed +++ b/tests/ui/needless_parens_on_range_literals.fixed @@ -2,7 +2,7 @@ // edition:2018 #![warn(clippy::needless_parens_on_range_literals)] -#![allow(clippy::almost_complete_letter_range)] +#![allow(clippy::almost_complete_range)] fn main() { let _ = 'a'..='z'; diff --git a/tests/ui/needless_parens_on_range_literals.rs b/tests/ui/needless_parens_on_range_literals.rs index 7abb8a1adc1b..671c0009e23b 100644 --- a/tests/ui/needless_parens_on_range_literals.rs +++ b/tests/ui/needless_parens_on_range_literals.rs @@ -2,7 +2,7 @@ // edition:2018 #![warn(clippy::needless_parens_on_range_literals)] -#![allow(clippy::almost_complete_letter_range)] +#![allow(clippy::almost_complete_range)] fn main() { let _ = ('a')..=('z'); diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index f69982d63a89..beec42f08bb0 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -1,3 +1,4 @@ +#![feature(type_alias_impl_trait)] #![warn(clippy::new_ret_no_self)] #![allow(dead_code)] @@ -400,3 +401,25 @@ mod issue7344 { } } } + +mod issue10041 { + struct Bomb; + + impl Bomb { + // Hidden default generic paramter. + pub fn new() -> impl PartialOrd { + 0i32 + } + } + + // TAIT with self-referencing bounds + type X = impl std::ops::Add; + + struct Bomb2; + + impl Bomb2 { + pub fn new() -> X { + 0i32 + } + } +} diff --git a/tests/ui/new_ret_no_self.stderr b/tests/ui/new_ret_no_self.stderr index bc13be47927b..2eaebfb5cac5 100644 --- a/tests/ui/new_ret_no_self.stderr +++ b/tests/ui/new_ret_no_self.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:49:5 + --> $DIR/new_ret_no_self.rs:50:5 | LL | / pub fn new(_: String) -> impl R { LL | | S3 @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:81:5 + --> $DIR/new_ret_no_self.rs:82:5 | LL | / pub fn new() -> u32 { LL | | unimplemented!(); @@ -17,7 +17,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:90:5 + --> $DIR/new_ret_no_self.rs:91:5 | LL | / pub fn new(_: String) -> u32 { LL | | unimplemented!(); @@ -25,7 +25,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:126:5 + --> $DIR/new_ret_no_self.rs:127:5 | LL | / pub fn new() -> (u32, u32) { LL | | unimplemented!(); @@ -33,7 +33,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:153:5 + --> $DIR/new_ret_no_self.rs:154:5 | LL | / pub fn new() -> *mut V { LL | | unimplemented!(); @@ -41,7 +41,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:171:5 + --> $DIR/new_ret_no_self.rs:172:5 | LL | / pub fn new() -> Option { LL | | unimplemented!(); @@ -49,19 +49,19 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:224:9 + --> $DIR/new_ret_no_self.rs:225:9 | LL | fn new() -> String; | ^^^^^^^^^^^^^^^^^^^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:236:9 + --> $DIR/new_ret_no_self.rs:237:9 | LL | fn new(_: String) -> String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:271:9 + --> $DIR/new_ret_no_self.rs:272:9 | LL | / fn new() -> (u32, u32) { LL | | unimplemented!(); @@ -69,7 +69,7 @@ LL | | } | |_________^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:298:9 + --> $DIR/new_ret_no_self.rs:299:9 | LL | / fn new() -> *mut V { LL | | unimplemented!(); @@ -77,7 +77,7 @@ LL | | } | |_________^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:368:9 + --> $DIR/new_ret_no_self.rs:369:9 | LL | / fn new(t: T) -> impl Into { LL | | 1 @@ -85,12 +85,28 @@ LL | | } | |_________^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:389:9 + --> $DIR/new_ret_no_self.rs:390:9 | LL | / fn new(t: T) -> impl Trait2<(), i32> { LL | | unimplemented!() LL | | } | |_________^ -error: aborting due to 12 previous errors +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:410:9 + | +LL | / pub fn new() -> impl PartialOrd { +LL | | 0i32 +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:421:9 + | +LL | / pub fn new() -> X { +LL | | 0i32 +LL | | } + | |_________^ + +error: aborting due to 14 previous errors diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index 4c5846fe837e..bca777a890c3 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -39,8 +39,14 @@ static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static. +static mut STATIC_MUT_SLICE: &mut [u32] = &mut [0]; + fn main() { let false_positive: &'static str = "test"; + + unsafe { + STATIC_MUT_SLICE[0] = 0; + } } trait Bar { diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index 64a66be1a83c..afe7644816d2 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -39,8 +39,14 @@ static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. +static mut STATIC_MUT_SLICE: &'static mut [u32] = &mut [0]; + fn main() { let false_positive: &'static str = "test"; + + unsafe { + STATIC_MUT_SLICE[0] = 0; + } } trait Bar { diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 0938ebf783ff..b2cbd2d9d01b 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -97,10 +97,16 @@ LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removin | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> $DIR/redundant_static_lifetimes.rs:65:16 + --> $DIR/redundant_static_lifetimes.rs:42:31 + | +LL | static mut STATIC_MUT_SLICE: &'static mut [u32] = &mut [0]; + | -^^^^^^^---------- help: consider removing `'static`: `&mut [u32]` + +error: statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:71:16 | LL | static V: &'static u8 = &17; | -^^^^^^^--- help: consider removing `'static`: `&u8` -error: aborting due to 17 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 689928f04794..2f76b5752960 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -4,6 +4,7 @@ // run-rustfix +#![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_if_conditions)] #![allow(clippy::box_collection)] @@ -37,6 +38,7 @@ #![allow(temporary_cstring_as_ptr)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![warn(clippy::almost_complete_range)] #![warn(clippy::disallowed_names)] #![warn(clippy::blocks_in_if_conditions)] #![warn(clippy::blocks_in_if_conditions)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index b74aa650ffd4..699c0ff464e9 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -4,6 +4,7 @@ // run-rustfix +#![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_if_conditions)] #![allow(clippy::box_collection)] @@ -37,6 +38,7 @@ #![allow(temporary_cstring_as_ptr)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![warn(clippy::almost_complete_letter_range)] #![warn(clippy::blacklisted_name)] #![warn(clippy::block_in_if_condition_expr)] #![warn(clippy::block_in_if_condition_stmt)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 622a32c5908a..9af58dc75a68 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,244 +1,250 @@ +error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` + --> $DIR/rename.rs:41:9 + | +LL | #![warn(clippy::almost_complete_letter_range)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:40:9 + --> $DIR/rename.rs:42:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:41:9 + --> $DIR/rename.rs:43:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:42:9 + --> $DIR/rename.rs:44:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:43:9 + --> $DIR/rename.rs:45:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:44:9 + --> $DIR/rename.rs:46:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:45:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:46:9 + --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:47:9 + --> $DIR/rename.rs:49:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:48:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:49:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 40 previous errors +error: aborting due to 41 previous errors diff --git a/tests/ui/seek_to_start_instead_of_rewind.fixed b/tests/ui/seek_to_start_instead_of_rewind.fixed index 9d0d1124c460..713cff604a1d 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.fixed +++ b/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -70,6 +70,12 @@ fn seek_to_end(t: &mut T) { t.seek(SeekFrom::End(0)); } +// This should NOT trigger clippy warning because +// expr is used here +fn seek_to_start_in_let(t: &mut T) { + let a = t.seek(SeekFrom::Start(0)).unwrap(); +} + fn main() { let mut f = OpenOptions::new() .write(true) diff --git a/tests/ui/seek_to_start_instead_of_rewind.rs b/tests/ui/seek_to_start_instead_of_rewind.rs index c5bc57cc3a74..467003a1a66f 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.rs +++ b/tests/ui/seek_to_start_instead_of_rewind.rs @@ -70,6 +70,12 @@ fn seek_to_end(t: &mut T) { t.seek(SeekFrom::End(0)); } +// This should NOT trigger clippy warning because +// expr is used here +fn seek_to_start_in_let(t: &mut T) { + let a = t.seek(SeekFrom::Start(0)).unwrap(); +} + fn main() { let mut f = OpenOptions::new() .write(true) diff --git a/tests/ui/seek_to_start_instead_of_rewind.stderr b/tests/ui/seek_to_start_instead_of_rewind.stderr index 6cce025359fe..342ec00fe729 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.stderr +++ b/tests/ui/seek_to_start_instead_of_rewind.stderr @@ -13,7 +13,7 @@ LL | t.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` error: used `seek` to go to the start of the stream - --> $DIR/seek_to_start_instead_of_rewind.rs:128:7 + --> $DIR/seek_to_start_instead_of_rewind.rs:134:7 | LL | f.seek(SeekFrom::Start(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed new file mode 100644 index 000000000000..42e97e1ca358 --- /dev/null +++ b/tests/ui/semicolon_inside_block.fixed @@ -0,0 +1,85 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_inside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()); } + { m!(()); } + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + { unit_fn_block(); }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_inside_block.rs b/tests/ui/semicolon_inside_block.rs new file mode 100644 index 000000000000..f40848f702e1 --- /dev/null +++ b/tests/ui/semicolon_inside_block.rs @@ -0,0 +1,85 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_inside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()) }; + { m!(()); } + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + { unit_fn_block(); }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_inside_block.stderr b/tests/ui/semicolon_inside_block.stderr new file mode 100644 index 000000000000..48d3690e2bde --- /dev/null +++ b/tests/ui/semicolon_inside_block.stderr @@ -0,0 +1,54 @@ +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:39:5 + | +LL | { unit_fn_block() }; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::semicolon-inside-block` implied by `-D warnings` +help: put the `;` here + | +LL - { unit_fn_block() }; +LL + { unit_fn_block(); } + | + +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:40:5 + | +LL | unsafe { unit_fn_block() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: put the `;` here + | +LL - unsafe { unit_fn_block() }; +LL + unsafe { unit_fn_block(); } + | + +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:48:5 + | +LL | / { +LL | | unit_fn_block(); +LL | | unit_fn_block() +LL | | }; + | |______^ + | +help: put the `;` here + | +LL ~ unit_fn_block(); +LL ~ } + | + +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:61:5 + | +LL | { m!(()) }; + | ^^^^^^^^^^^ + | +help: put the `;` here + | +LL - { m!(()) }; +LL + { m!(()); } + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/semicolon_outside_block.fixed b/tests/ui/semicolon_outside_block.fixed new file mode 100644 index 000000000000..091eaa7518e9 --- /dev/null +++ b/tests/ui/semicolon_outside_block.fixed @@ -0,0 +1,85 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_outside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()) }; + { m!(()) }; + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + { unit_fn_block(); }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_outside_block.rs b/tests/ui/semicolon_outside_block.rs new file mode 100644 index 000000000000..7ce46431fac9 --- /dev/null +++ b/tests/ui/semicolon_outside_block.rs @@ -0,0 +1,85 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_outside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()) }; + { m!(()); } + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + { unit_fn_block(); }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_outside_block.stderr b/tests/ui/semicolon_outside_block.stderr new file mode 100644 index 000000000000..dcc102e60994 --- /dev/null +++ b/tests/ui/semicolon_outside_block.stderr @@ -0,0 +1,54 @@ +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:42:5 + | +LL | { unit_fn_block(); } + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::semicolon-outside-block` implied by `-D warnings` +help: put the `;` here + | +LL - { unit_fn_block(); } +LL + { unit_fn_block() }; + | + +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:43:5 + | +LL | unsafe { unit_fn_block(); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: put the `;` here + | +LL - unsafe { unit_fn_block(); } +LL + unsafe { unit_fn_block() }; + | + +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:52:5 + | +LL | / { +LL | | unit_fn_block(); +LL | | unit_fn_block(); +LL | | } + | |_____^ + | +help: put the `;` here + | +LL ~ unit_fn_block() +LL ~ }; + | + +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:62:5 + | +LL | { m!(()); } + | ^^^^^^^^^^^ + | +help: put the `;` here + | +LL - { m!(()); } +LL + { m!(()) }; + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/string_lit_as_bytes.fixed b/tests/ui/string_lit_as_bytes.fixed index df2256e4f97d..506187fc1257 100644 --- a/tests/ui/string_lit_as_bytes.fixed +++ b/tests/ui/string_lit_as_bytes.fixed @@ -25,6 +25,12 @@ fn str_lit_as_bytes() { let includestr = include_bytes!("string_lit_as_bytes.rs"); let _ = b"string with newline\t\n"; + + let _ = match "x".as_bytes() { + b"xx" => 0, + [b'x', ..] => 1, + _ => 2, + }; } fn main() {} diff --git a/tests/ui/string_lit_as_bytes.rs b/tests/ui/string_lit_as_bytes.rs index c6bf8f732ed9..2c339f1ddb81 100644 --- a/tests/ui/string_lit_as_bytes.rs +++ b/tests/ui/string_lit_as_bytes.rs @@ -25,6 +25,12 @@ fn str_lit_as_bytes() { let includestr = include_str!("string_lit_as_bytes.rs").as_bytes(); let _ = "string with newline\t\n".as_bytes(); + + let _ = match "x".as_bytes() { + b"xx" => 0, + [b'x', ..] => 1, + _ => 2, + }; } fn main() {} diff --git a/tests/ui/uninlined_format_args_panic.edition2018.fixed b/tests/ui/uninlined_format_args_panic.edition2018.fixed index 96cc0877960e..52b5343c351e 100644 --- a/tests/ui/uninlined_format_args_panic.edition2018.fixed +++ b/tests/ui/uninlined_format_args_panic.edition2018.fixed @@ -26,4 +26,7 @@ fn main() { panic!("p4 {var}"); } } + + assert!(var == 1, "p5 {}", var); + debug_assert!(var == 1, "p6 {}", var); } diff --git a/tests/ui/uninlined_format_args_panic.edition2021.fixed b/tests/ui/uninlined_format_args_panic.edition2021.fixed index faf8ca4d3a79..ee72065e28ab 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.fixed +++ b/tests/ui/uninlined_format_args_panic.edition2021.fixed @@ -26,4 +26,7 @@ fn main() { panic!("p4 {var}"); } } + + assert!(var == 1, "p5 {var}"); + debug_assert!(var == 1, "p6 {var}"); } diff --git a/tests/ui/uninlined_format_args_panic.edition2021.stderr b/tests/ui/uninlined_format_args_panic.edition2021.stderr index 0f09c45f4132..fc7b125080e7 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.stderr +++ b/tests/ui/uninlined_format_args_panic.edition2021.stderr @@ -47,5 +47,29 @@ LL - panic!("p3 {var}", var = var); LL + panic!("p3 {var}"); | -error: aborting due to 4 previous errors +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:30:5 + | +LL | assert!(var == 1, "p5 {}", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - assert!(var == 1, "p5 {}", var); +LL + assert!(var == 1, "p5 {var}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:31:5 + | +LL | debug_assert!(var == 1, "p6 {}", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - debug_assert!(var == 1, "p6 {}", var); +LL + debug_assert!(var == 1, "p6 {var}"); + | + +error: aborting due to 6 previous errors diff --git a/tests/ui/uninlined_format_args_panic.rs b/tests/ui/uninlined_format_args_panic.rs index 6421c5bbed2f..b4a0a0f496e4 100644 --- a/tests/ui/uninlined_format_args_panic.rs +++ b/tests/ui/uninlined_format_args_panic.rs @@ -26,4 +26,7 @@ fn main() { panic!("p4 {var}"); } } + + assert!(var == 1, "p5 {}", var); + debug_assert!(var == 1, "p6 {}", var); } diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index ddeda795f817..345f6d604c4f 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -454,3 +454,23 @@ mod issue_9771b { Key(v.to_vec()) } } + +// This is a watered down version of the code in: https://github.com/oxigraph/rio +// The ICE is triggered by the call to `to_owned` on this line: +// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116 +mod issue_10021 { + #![allow(unused)] + + pub struct Iri(T); + + impl> Iri { + pub fn parse(iri: T) -> Result { + unimplemented!() + } + } + + pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> { + let base_iri = Iri::parse(url.to_owned())?; + Ok(()) + } +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 95d2576733cd..7eb53df39e5b 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -454,3 +454,23 @@ mod issue_9771b { Key(v.to_vec()) } } + +// This is a watered down version of the code in: https://github.com/oxigraph/rio +// The ICE is triggered by the call to `to_owned` on this line: +// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116 +mod issue_10021 { + #![allow(unused)] + + pub struct Iri(T); + + impl> Iri { + pub fn parse(iri: T) -> Result { + unimplemented!() + } + } + + pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> { + let base_iri = Iri::parse(url.to_owned())?; + Ok(()) + } +} diff --git a/tests/ui/zero_ptr_no_std.fixed b/tests/ui/zero_ptr_no_std.fixed new file mode 100644 index 000000000000..8906c776977a --- /dev/null +++ b/tests/ui/zero_ptr_no_std.fixed @@ -0,0 +1,21 @@ +// run-rustfix + +#![feature(lang_items, start, libc)] +#![no_std] +#![deny(clippy::zero_ptr)] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _ = core::ptr::null::(); + let _ = core::ptr::null_mut::(); + let _: *const u8 = core::ptr::null(); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/zero_ptr_no_std.rs b/tests/ui/zero_ptr_no_std.rs new file mode 100644 index 000000000000..379c1b18d299 --- /dev/null +++ b/tests/ui/zero_ptr_no_std.rs @@ -0,0 +1,21 @@ +// run-rustfix + +#![feature(lang_items, start, libc)] +#![no_std] +#![deny(clippy::zero_ptr)] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _ = 0 as *const usize; + let _ = 0 as *mut f64; + let _: *const u8 = 0 as *const _; + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/zero_ptr_no_std.stderr b/tests/ui/zero_ptr_no_std.stderr new file mode 100644 index 000000000000..d92bb4a6528d --- /dev/null +++ b/tests/ui/zero_ptr_no_std.stderr @@ -0,0 +1,26 @@ +error: `0 as *const _` detected + --> $DIR/zero_ptr_no_std.rs:9:13 + | +LL | let _ = 0 as *const usize; + | ^^^^^^^^^^^^^^^^^ help: try: `core::ptr::null::()` + | +note: the lint level is defined here + --> $DIR/zero_ptr_no_std.rs:5:9 + | +LL | #![deny(clippy::zero_ptr)] + | ^^^^^^^^^^^^^^^^ + +error: `0 as *mut _` detected + --> $DIR/zero_ptr_no_std.rs:10:13 + | +LL | let _ = 0 as *mut f64; + | ^^^^^^^^^^^^^ help: try: `core::ptr::null_mut::()` + +error: `0 as *const _` detected + --> $DIR/zero_ptr_no_std.rs:11:24 + | +LL | let _: *const u8 = 0 as *const _; + | ^^^^^^^^^^^^^ help: try: `core::ptr::null()` + +error: aborting due to 3 previous errors + diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 7a85386a3df4..c721e9969c9a 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -2,7 +2,6 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::single_match_else)] -use rustc_tools_util::VersionInfo; use std::fs; #[test] diff --git a/triagebot.toml b/triagebot.toml index acb476ee6962..6f50ef932e11 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1,7 +1,7 @@ [relabel] allow-unauthenticated = [ "A-*", "C-*", "E-*", "I-*", "L-*", "P-*", "S-*", "T-*", - "good-first-issue" + "good-first-issue", "beta-nominated" ] # Allows shortcuts like `@rustbot ready` From af39a8a4a82001b38e2b6c0d391d1aa76740ec4b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 1 Dec 2022 17:58:28 +0100 Subject: [PATCH 360/524] Identify more cases of useless `into_iter()` calls If the type of the result of a call to `IntoIterator::into_iter()` and the type of the receiver are the same, then the receiver implements `Iterator` and `into_iter()` is the identity function. The call to `into_iter()` may be removed in all but two cases: - If the receiver implements `Copy`, `into_iter()` will produce a copy of the receiver and cannot be removed. For example, `x.into_iter().next()` will not advance `x` while `x.next()` will. - If the receiver is an immutable local variable and the call to `into_iter()` appears in a larger expression, removing the call to `into_iter()` might cause mutability issues. For example, if `x` is an immutable local variable, `x.into_iter().next()` will compile while `x.next()` will not as `next()` receives `&mut self`. --- clippy_lints/src/useless_conversion.rs | 28 ++++++---- tests/ui/useless_conversion.fixed | 73 ++++++++++++++++++++++++-- tests/ui/useless_conversion.rs | 73 ++++++++++++++++++++++++-- tests/ui/useless_conversion.stderr | 54 ++++++++++++++----- 4 files changed, 200 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 3743d5d97a73..a95e7b613746 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; -use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -81,16 +81,24 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } if is_trait_method(cx, e, sym::IntoIterator) && name.ident.name == sym::into_iter { - if let Some(parent_expr) = get_parent_expr(cx, e) { - if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind { - if parent_name.ident.name != sym::into_iter { - return; - } - } + if get_parent_expr(cx, e).is_some() && + let Some(id) = path_to_local(recv) && + let Node::Pat(pat) = cx.tcx.hir().get(id) && + let PatKind::Binding(ann, ..) = pat.kind && + ann != BindingAnnotation::MUT + { + // Do not remove .into_iter() applied to a non-mutable local variable used in + // a larger expression context as it would differ in mutability. + return; } + let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); - if same_type_and_consts(a, b) { + + // If the types are identical then .into_iter() can be removed, unless the type + // implements Copy, in which case .into_iter() returns a copy of the receiver and + // cannot be safely omitted. + if same_type_and_consts(a, b) && !is_copy(cx, b) { let sugg = snippet(cx, recv.span, "").into_owned(); span_lint_and_sugg( cx, diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 70ff08f36551..94b206d8e58f 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -33,12 +33,71 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } -fn test_issue_5833() -> Result<(), ()> { +fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); if Some("ok") == lines.into_iter().next() {} +} - Ok(()) +fn lint_into_iter_on_mutable_local_implementing_iterator_in_expr() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator_2() { + let text = "foo\r\nbar\n\nbaz\n"; + if Some("ok") == text.lines().next() {} +} + +#[allow(const_item_mutation)] +fn lint_into_iter_on_const_implementing_iterator() { + const NUMBERS: std::ops::Range = 0..10; + let _ = NUMBERS.next(); +} + +fn lint_into_iter_on_const_implementing_iterator_2() { + const NUMBERS: std::ops::Range = 0..10; + let mut n = NUMBERS; + n.next(); +} + +#[derive(Clone, Copy)] +struct CopiableCounter { + counter: u32, +} + +impl Iterator for CopiableCounter { + type Item = u32; + + fn next(&mut self) -> Option { + self.counter = self.counter.wrapping_add(1); + Some(self.counter) + } +} + +fn dont_lint_into_iter_on_copy_iter() { + let mut c = CopiableCounter { counter: 0 }; + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.next(), Some(1)); + assert_eq!(c.next(), Some(2)); +} + +fn dont_lint_into_iter_on_static_copy_iter() { + static mut C: CopiableCounter = CopiableCounter { counter: 0 }; + unsafe { + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.next(), Some(1)); + assert_eq!(C.next(), Some(2)); + } } fn main() { @@ -46,7 +105,15 @@ fn main() { test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); - test_issue_5833().unwrap(); + + dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_expr_implementing_iterator(); + lint_into_iter_on_expr_implementing_iterator_2(); + lint_into_iter_on_const_implementing_iterator(); + lint_into_iter_on_const_implementing_iterator_2(); + dont_lint_into_iter_on_copy_iter(); + dont_lint_into_iter_on_static_copy_iter(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index f2444a8f436b..c7ae927941bf 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -33,12 +33,71 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } -fn test_issue_5833() -> Result<(), ()> { +fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); if Some("ok") == lines.into_iter().next() {} +} - Ok(()) +fn lint_into_iter_on_mutable_local_implementing_iterator_in_expr() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines().into_iter(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator_2() { + let text = "foo\r\nbar\n\nbaz\n"; + if Some("ok") == text.lines().into_iter().next() {} +} + +#[allow(const_item_mutation)] +fn lint_into_iter_on_const_implementing_iterator() { + const NUMBERS: std::ops::Range = 0..10; + let _ = NUMBERS.into_iter().next(); +} + +fn lint_into_iter_on_const_implementing_iterator_2() { + const NUMBERS: std::ops::Range = 0..10; + let mut n = NUMBERS.into_iter(); + n.next(); +} + +#[derive(Clone, Copy)] +struct CopiableCounter { + counter: u32, +} + +impl Iterator for CopiableCounter { + type Item = u32; + + fn next(&mut self) -> Option { + self.counter = self.counter.wrapping_add(1); + Some(self.counter) + } +} + +fn dont_lint_into_iter_on_copy_iter() { + let mut c = CopiableCounter { counter: 0 }; + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.next(), Some(1)); + assert_eq!(c.next(), Some(2)); +} + +fn dont_lint_into_iter_on_static_copy_iter() { + static mut C: CopiableCounter = CopiableCounter { counter: 0 }; + unsafe { + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.next(), Some(1)); + assert_eq!(C.next(), Some(2)); + } } fn main() { @@ -46,7 +105,15 @@ fn main() { test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); - test_issue_5833().unwrap(); + + dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_expr_implementing_iterator(); + lint_into_iter_on_expr_implementing_iterator_2(); + lint_into_iter_on_const_implementing_iterator(); + lint_into_iter_on_const_implementing_iterator_2(); + dont_lint_into_iter_on_copy_iter(); + dont_lint_into_iter_on_static_copy_iter(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 65ee3807fa9d..be067c6843ac 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -22,71 +22,101 @@ error: useless conversion to the same type: `i32` LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:45:22 + | +LL | if Some("ok") == lines.into_iter().next() {} + | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `lines` + +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:50:21 + | +LL | let mut lines = text.lines().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` + +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:56:22 + | +LL | if Some("ok") == text.lines().into_iter().next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` + +error: useless conversion to the same type: `std::ops::Range` + --> $DIR/useless_conversion.rs:62:13 + | +LL | let _ = NUMBERS.into_iter().next(); + | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` + +error: useless conversion to the same type: `std::ops::Range` + --> $DIR/useless_conversion.rs:67:17 + | +LL | let mut n = NUMBERS.into_iter(); + | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` + error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:61:21 + --> $DIR/useless_conversion.rs:128:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:62:21 + --> $DIR/useless_conversion.rs:129:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:63:13 + --> $DIR/useless_conversion.rs:130:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:64:13 + --> $DIR/useless_conversion.rs:131:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:65:13 + --> $DIR/useless_conversion.rs:132:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> $DIR/useless_conversion.rs:66:13 + --> $DIR/useless_conversion.rs:133:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:67:21 + --> $DIR/useless_conversion.rs:134:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> $DIR/useless_conversion.rs:72:13 + --> $DIR/useless_conversion.rs:139:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:78:23 + --> $DIR/useless_conversion.rs:145:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:80:13 + --> $DIR/useless_conversion.rs:147:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter>` - --> $DIR/useless_conversion.rs:82:13 + --> $DIR/useless_conversion.rs:149:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` -error: aborting due to 14 previous errors +error: aborting due to 19 previous errors From 6afe5471cf3cc78b5ce85d1bbb13a010516c500e Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sat, 17 Dec 2022 20:22:52 +0300 Subject: [PATCH 361/524] Add lint `transmute_null_to_fn` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/transmute/mod.rs | 31 ++++++++++ .../src/transmute/transmute_null_to_fn.rs | 62 +++++++++++++++++++ tests/ui/transmute_null_to_fn.rs | 28 +++++++++ tests/ui/transmute_null_to_fn.stderr | 27 ++++++++ 6 files changed, 150 insertions(+) create mode 100644 clippy_lints/src/transmute/transmute_null_to_fn.rs create mode 100644 tests/ui/transmute_null_to_fn.rs create mode 100644 tests/ui/transmute_null_to_fn.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 903ee938d9d2..3c2801cfb5e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4590,6 +4590,7 @@ Released 2018-09-13 [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool [`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char [`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float +[`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn [`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes [`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr [`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 3cd7d1d7e722..5bae62ce24f0 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -568,6 +568,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, + crate::transmute::TRANSMUTE_NULL_TO_FN_INFO, crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 83e651aba8e8..7a2ab2bb4c40 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -3,6 +3,7 @@ mod transmute_float_to_int; mod transmute_int_to_bool; mod transmute_int_to_char; mod transmute_int_to_float; +mod transmute_null_to_fn; mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; mod transmute_ptr_to_ref; @@ -409,6 +410,34 @@ declare_clippy_lint! { "transmutes from a null pointer to a reference, which is undefined behavior" } +declare_clippy_lint! { + /// ### What it does + /// Checks for null function pointer creation through transmute. + /// + /// ### Why is this bad? + /// Creating a null function pointer is undefined behavior. + /// + /// More info: https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization + /// + /// ### Known problems + /// Not all cases can be detected at the moment of this writing. + /// For example, variables which hold a null pointer and are then fed to a `transmute` + /// call, aren't detectable yet. + /// + /// ### Example + /// ```rust + /// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null() ) }; + /// ``` + /// Use instead: + /// ```rust + /// let null_fn: Option = None; + /// ``` + #[clippy::version = "1.67.0"] + pub TRANSMUTE_NULL_TO_FN, + correctness, + "transmute results in a null function pointer, which is undefined behavior" +} + pub struct Transmute { msrv: Msrv, } @@ -428,6 +457,7 @@ impl_lint_pass!(Transmute => [ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, TRANSMUTING_NULL, + TRANSMUTE_NULL_TO_FN, ]); impl Transmute { #[must_use] @@ -461,6 +491,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmuting_null::check(cx, e, arg, to_ty) + | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs new file mode 100644 index 000000000000..db89078f657e --- /dev/null +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -0,0 +1,62 @@ +use clippy_utils::consts::{constant_context, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::symbol::sym; + +use super::TRANSMUTE_NULL_TO_FN; + +const LINT_MSG: &str = "transmuting a known null pointer into a function pointer"; +const NOTE_MSG: &str = "this transmute results in a null function pointer"; +const HELP_MSG: &str = + "try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value"; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool { + if !to_ty.is_fn() { + return false; + } + + // Catching transmute over constants that resolve to `null`. + let mut const_eval_context = constant_context(cx, cx.typeck_results()); + if let ExprKind::Path(ref _qpath) = arg.kind && + let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) && + x == 0 + { + span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| { + diag.span_label(expr.span, NOTE_MSG); + diag.help(HELP_MSG); + }); + return true; + } + + // Catching: + // `std::mem::transmute(0 as *const i32)` + if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind && is_integer_literal(inner_expr, 0) { + span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| { + diag.span_label(expr.span, NOTE_MSG); + diag.help(HELP_MSG); + }); + return true; + } + + // Catching: + // `std::mem::transmute(std::ptr::null::())` + if let ExprKind::Call(func1, []) = arg.kind && + is_path_diagnostic_item(cx, func1, sym::ptr_null) + { + span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| { + diag.span_label(expr.span, NOTE_MSG); + diag.help(HELP_MSG); + }); + return true; + } + + // FIXME: + // Also catch transmutations of variables which are known nulls. + // To do this, MIR const propagation seems to be the better tool. + // Whenever MIR const prop routines are more developed, this will + // become available. As of this writing (25/03/19) it is not yet. + false +} diff --git a/tests/ui/transmute_null_to_fn.rs b/tests/ui/transmute_null_to_fn.rs new file mode 100644 index 000000000000..b3ea3d9039e0 --- /dev/null +++ b/tests/ui/transmute_null_to_fn.rs @@ -0,0 +1,28 @@ +#![allow(dead_code)] +#![warn(clippy::transmute_null_to_fn)] +#![allow(clippy::zero_ptr)] + +// Easy to lint because these only span one line. +fn one_liners() { + unsafe { + let _: fn() = std::mem::transmute(0 as *const ()); + let _: fn() = std::mem::transmute(std::ptr::null::<()>()); + } +} + +pub const ZPTR: *const usize = 0 as *const _; +pub const NOT_ZPTR: *const usize = 1 as *const _; + +fn transmute_const() { + unsafe { + // Should raise a lint. + let _: fn() = std::mem::transmute(ZPTR); + // Should NOT raise a lint. + let _: fn() = std::mem::transmute(NOT_ZPTR); + } +} + +fn main() { + one_liners(); + transmute_const(); +} diff --git a/tests/ui/transmute_null_to_fn.stderr b/tests/ui/transmute_null_to_fn.stderr new file mode 100644 index 000000000000..f0c65497d750 --- /dev/null +++ b/tests/ui/transmute_null_to_fn.stderr @@ -0,0 +1,27 @@ +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:8:23 + | +LL | let _: fn() = std::mem::transmute(0 as *const ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + = note: `-D clippy::transmute-null-to-fn` implied by `-D warnings` + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:9:23 + | +LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:19:23 + | +LL | let _: fn() = std::mem::transmute(ZPTR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: aborting due to 3 previous errors + From 3cc67d08569b5cf847242532f99c5aca838dbdeb Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sun, 18 Dec 2022 02:14:09 +0300 Subject: [PATCH 362/524] Relax clippy_utils::consts::miri_to_const pointer type restrictiveness --- clippy_lints/src/lib.rs | 2 ++ clippy_utils/src/consts.rs | 7 +------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 39850d598038..7f7d73339e45 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -125,6 +125,7 @@ mod explicit_write; mod fallible_impl_from; mod float_literal; mod floating_point_arithmetic; +mod fn_null_check; mod format; mod format_args; mod format_impl; @@ -902,6 +903,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock)); + store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 7a637d32babe..a67bd8d46006 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -620,12 +620,7 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) - ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( int.try_into().expect("invalid f64 bit representation"), ))), - ty::RawPtr(type_and_mut) => { - if let ty::Uint(_) = type_and_mut.ty.kind() { - return Some(Constant::RawPtr(int.assert_bits(int.size()))); - } - None - }, + ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), // FIXME: implement other conversions. _ => None, } From 42106e04951f409c88131a42a0514af479ecb932 Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sun, 18 Dec 2022 02:16:04 +0300 Subject: [PATCH 363/524] Add lint `fn_null_check` --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/fn_null_check.rs | 129 ++++++++++++++++++ .../src/transmute/transmute_null_to_fn.rs | 2 +- tests/ui/fn_null_check.rs | 21 +++ tests/ui/fn_null_check.stderr | 43 ++++++ 6 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/fn_null_check.rs create mode 100644 tests/ui/fn_null_check.rs create mode 100644 tests/ui/fn_null_check.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c2801cfb5e3..17ff182c7bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4203,6 +4203,7 @@ Released 2018-09-13 [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const [`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons +[`fn_null_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_null_check [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast [`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 5bae62ce24f0..480a65ac70ca 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -161,6 +161,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO, crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO, + crate::fn_null_check::FN_NULL_CHECK_INFO, crate::format::USELESS_FORMAT_INFO, crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO, crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO, diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs new file mode 100644 index 000000000000..d0e82500dd0c --- /dev/null +++ b/clippy_lints/src/fn_null_check.rs @@ -0,0 +1,129 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use if_chain::if_chain; +use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for comparing a function pointer to null. + /// + /// ### Why is this bad? + /// Function pointers are assumed to not be null. + /// + /// ### Example + /// ```rust,ignore + /// let fn_ptr: fn() = /* somehow obtained nullable function pointer */ + /// + /// if (fn_ptr as *const ()).is_null() { ... } + /// ``` + /// Use instead: + /// ```rust + /// let fn_ptr: Option = /* somehow obtained nullable function pointer */ + /// + /// if fn_ptr.is_none() { ... } + /// ``` + #[clippy::version = "1.67.0"] + pub FN_NULL_CHECK, + correctness, + "`fn()` type assumed to be nullable" +} +declare_lint_pass!(FnNullCheck => [FN_NULL_CHECK]); + +const LINT_MSG: &str = "function pointer assumed to be nullable, even though it isn't"; +const HELP_MSG: &str = "try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value"; + +fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if_chain! { + if let ExprKind::Cast(cast_expr, cast_ty) = expr.kind; + if let TyKind::Ptr(_) = cast_ty.kind; + if cx.typeck_results().expr_ty_adjusted(cast_expr).is_fn(); + then { + true + } else { + false + } + } +} + +impl<'tcx> LateLintPass<'tcx> for FnNullCheck { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // Catching: + // (fn_ptr as * ).is_null() + if_chain! { + if let ExprKind::MethodCall(method_name, receiver, _, _) = expr.kind; + if method_name.ident.as_str() == "is_null"; + if is_fn_ptr_cast(cx, receiver); + then { + span_lint_and_help( + cx, + FN_NULL_CHECK, + expr.span, + LINT_MSG, + None, + HELP_MSG + ); + } + } + + if let ExprKind::Binary(op, left, right) = expr.kind + && let BinOpKind::Eq = op.node + { + let to_check: &Expr<'_>; + if is_fn_ptr_cast(cx, left) { + to_check = right; + } else if is_fn_ptr_cast(cx, right) { + to_check = left; + } else { + return; + } + + // Catching: + // (fn_ptr as * ) == + let c = constant(cx, cx.typeck_results(), to_check); + if let Some((Constant::RawPtr(0), _)) = c { + span_lint_and_help( + cx, + FN_NULL_CHECK, + expr.span, + LINT_MSG, + None, + HELP_MSG + ); + return; + } + + // Catching: + // (fn_ptr as * ) == (0 as ) + if let ExprKind::Cast(cast_expr, _) = to_check.kind && is_integer_literal(cast_expr, 0) { + span_lint_and_help( + cx, + FN_NULL_CHECK, + expr.span, + LINT_MSG, + None, + HELP_MSG + ); + return; + } + + // Catching: + // (fn_ptr as * ) == std::ptr::null() + if let ExprKind::Call(func, []) = to_check.kind && + is_path_diagnostic_item(cx, func, sym::ptr_null) + { + span_lint_and_help( + cx, + FN_NULL_CHECK, + expr.span, + LINT_MSG, + None, + HELP_MSG + ); + } + } + } +} diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index db89078f657e..032b4c1e2d20 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -9,7 +9,7 @@ use rustc_span::symbol::sym; use super::TRANSMUTE_NULL_TO_FN; const LINT_MSG: &str = "transmuting a known null pointer into a function pointer"; -const NOTE_MSG: &str = "this transmute results in a null function pointer"; +const NOTE_MSG: &str = "this transmute results in undefined behavior"; const HELP_MSG: &str = "try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value"; diff --git a/tests/ui/fn_null_check.rs b/tests/ui/fn_null_check.rs new file mode 100644 index 000000000000..df5bc8420d57 --- /dev/null +++ b/tests/ui/fn_null_check.rs @@ -0,0 +1,21 @@ +#![allow(unused)] +#![warn(clippy::fn_null_check)] +#![allow(clippy::cmp_null)] +#![allow(clippy::ptr_eq)] +#![allow(clippy::zero_ptr)] + +pub const ZPTR: *const () = 0 as *const _; +pub const NOT_ZPTR: *const () = 1 as *const _; + +fn main() { + let fn_ptr = main; + + if (fn_ptr as *mut ()).is_null() {} + if (fn_ptr as *const u8).is_null() {} + if (fn_ptr as *const ()) == std::ptr::null() {} + if (fn_ptr as *const ()) == (0 as *const ()) {} + if (fn_ptr as *const ()) == ZPTR {} + + // no lint + if (fn_ptr as *const ()) == NOT_ZPTR {} +} diff --git a/tests/ui/fn_null_check.stderr b/tests/ui/fn_null_check.stderr new file mode 100644 index 000000000000..660dd3239792 --- /dev/null +++ b/tests/ui/fn_null_check.stderr @@ -0,0 +1,43 @@ +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:13:8 + | +LL | if (fn_ptr as *mut ()).is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + = note: `-D clippy::fn-null-check` implied by `-D warnings` + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:14:8 + | +LL | if (fn_ptr as *const u8).is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:15:8 + | +LL | if (fn_ptr as *const ()) == std::ptr::null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:16:8 + | +LL | if (fn_ptr as *const ()) == (0 as *const ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:17:8 + | +LL | if (fn_ptr as *const ()) == ZPTR {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: aborting due to 5 previous errors + From 54a9168efb9d1a34e8430199d20a7fd5db032e7b Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sun, 18 Dec 2022 02:25:21 +0300 Subject: [PATCH 364/524] Remove useless pattern matching --- clippy_lints/src/transmute/transmute_null_to_fn.rs | 3 +-- clippy_lints/src/transmute/transmuting_null.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 032b4c1e2d20..2286e17fd169 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -21,8 +21,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); if let ExprKind::Path(ref _qpath) = arg.kind && - let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) && - x == 0 + let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg) { span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| { diag.span_label(expr.span, NOTE_MSG); diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index 19ce5ae72c24..1e407fc4138c 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -18,8 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); if let ExprKind::Path(ref _qpath) = arg.kind && - let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) && - x == 0 + let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; From dae54fad3ee4b51ed286d4686f6d064b48b56cca Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sun, 18 Dec 2022 03:20:04 +0300 Subject: [PATCH 365/524] Doc codeblock fixup --- clippy_lints/src/fn_null_check.rs | 2 +- clippy_lints/src/transmute/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs index d0e82500dd0c..89841936954d 100644 --- a/clippy_lints/src/fn_null_check.rs +++ b/clippy_lints/src/fn_null_check.rs @@ -21,7 +21,7 @@ declare_clippy_lint! { /// if (fn_ptr as *const ()).is_null() { ... } /// ``` /// Use instead: - /// ```rust + /// ```rust,ignore /// let fn_ptr: Option = /* somehow obtained nullable function pointer */ /// /// if fn_ptr.is_none() { ... } diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 7a2ab2bb4c40..691d759d7739 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -426,7 +426,7 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null() ) }; + /// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null::<()>() ) }; /// ``` /// Use instead: /// ```rust From ebb0759bb3a9341ff662551366efb1844c91d3ca Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 11 Dec 2022 12:20:46 +0900 Subject: [PATCH 366/524] Add a test for regular wildcard fix --- tests/ui/match_wildcard_for_single_variants.fixed | 6 ++++++ tests/ui/match_wildcard_for_single_variants.rs | 6 ++++++ tests/ui/match_wildcard_for_single_variants.stderr | 8 +++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index c508b7cc3b2a..fc252cdd3529 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -146,5 +146,11 @@ mod issue9993 { Foo::A(false) => 2, Foo::B => 3, }; + + let _ = match Foo::B { + _ if false => 0, + Foo::A(_) => 1, + Foo::B => 2, + }; } } diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index ad03f7971297..9a5c849e6ec9 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -146,5 +146,11 @@ mod issue9993 { Foo::A(false) => 2, Foo::B => 3, }; + + let _ = match Foo::B { + _ if false => 0, + Foo::A(_) => 1, + _ => 2, + }; } } diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 34538dea8e5f..6fa313dc9111 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -48,5 +48,11 @@ error: wildcard matches only a single variant and will also match any future add LL | _ => (), | ^ help: try this: `Color::Blue` -error: aborting due to 8 previous errors +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:153:13 + | +LL | _ => 2, + | ^ help: try this: `Foo::B` + +error: aborting due to 9 previous errors From b1ca307168b622d66a7f0d66421933236bb8cbcf Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sun, 18 Dec 2022 18:58:15 +0300 Subject: [PATCH 367/524] Address some of the code style issues --- clippy_lints/src/fn_null_check.rs | 75 +++++++------------ .../src/transmute/transmute_null_to_fn.rs | 3 +- 2 files changed, 27 insertions(+), 51 deletions(-) diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs index 89841936954d..f4b7a55fa746 100644 --- a/clippy_lints/src/fn_null_check.rs +++ b/clippy_lints/src/fn_null_check.rs @@ -1,7 +1,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; -use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -33,19 +32,24 @@ declare_clippy_lint! { } declare_lint_pass!(FnNullCheck => [FN_NULL_CHECK]); -const LINT_MSG: &str = "function pointer assumed to be nullable, even though it isn't"; -const HELP_MSG: &str = "try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value"; +fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { + span_lint_and_help( + cx, + FN_NULL_CHECK, + expr.span, + "function pointer assumed to be nullable, even though it isn't", + None, + "try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value", + ) +} fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::Cast(cast_expr, cast_ty) = expr.kind; - if let TyKind::Ptr(_) = cast_ty.kind; - if cx.typeck_results().expr_ty_adjusted(cast_expr).is_fn(); - then { - true - } else { - false - } + if let ExprKind::Cast(cast_expr, cast_ty) = expr.kind + && let TyKind::Ptr(_) = cast_ty.kind + { + cx.typeck_results().expr_ty_adjusted(cast_expr).is_fn() + } else { + false } } @@ -53,20 +57,12 @@ impl<'tcx> LateLintPass<'tcx> for FnNullCheck { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Catching: // (fn_ptr as * ).is_null() - if_chain! { - if let ExprKind::MethodCall(method_name, receiver, _, _) = expr.kind; - if method_name.ident.as_str() == "is_null"; - if is_fn_ptr_cast(cx, receiver); - then { - span_lint_and_help( - cx, - FN_NULL_CHECK, - expr.span, - LINT_MSG, - None, - HELP_MSG - ); - } + if let ExprKind::MethodCall(method_name, receiver, _, _) = expr.kind + && method_name.ident.as_str() == "is_null" + && is_fn_ptr_cast(cx, receiver) + { + lint_expr(cx, expr); + return; } if let ExprKind::Binary(op, left, right) = expr.kind @@ -85,28 +81,14 @@ impl<'tcx> LateLintPass<'tcx> for FnNullCheck { // (fn_ptr as * ) == let c = constant(cx, cx.typeck_results(), to_check); if let Some((Constant::RawPtr(0), _)) = c { - span_lint_and_help( - cx, - FN_NULL_CHECK, - expr.span, - LINT_MSG, - None, - HELP_MSG - ); + lint_expr(cx, expr); return; } // Catching: // (fn_ptr as * ) == (0 as ) if let ExprKind::Cast(cast_expr, _) = to_check.kind && is_integer_literal(cast_expr, 0) { - span_lint_and_help( - cx, - FN_NULL_CHECK, - expr.span, - LINT_MSG, - None, - HELP_MSG - ); + lint_expr(cx, expr); return; } @@ -115,14 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for FnNullCheck { if let ExprKind::Call(func, []) = to_check.kind && is_path_diagnostic_item(cx, func, sym::ptr_null) { - span_lint_and_help( - cx, - FN_NULL_CHECK, - expr.span, - LINT_MSG, - None, - HELP_MSG - ); + lint_expr(cx, expr); } } } diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 2286e17fd169..074a5d317638 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -18,7 +18,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t return false; } - // Catching transmute over constants that resolve to `null`. + // Catching: + // transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); if let ExprKind::Path(ref _qpath) = arg.kind && let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg) From 9b2fc8e2a2da497344d42ccf8ddefc794eb82858 Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sun, 18 Dec 2022 19:43:26 +0300 Subject: [PATCH 368/524] Make clippy happy --- clippy_lints/src/fn_null_check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs index f4b7a55fa746..6eb5ddd94d7b 100644 --- a/clippy_lints/src/fn_null_check.rs +++ b/clippy_lints/src/fn_null_check.rs @@ -40,7 +40,7 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { "function pointer assumed to be nullable, even though it isn't", None, "try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value", - ) + ); } fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { From 20f501a9d9f253bfe503388755bdebfe72910e0e Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sun, 18 Dec 2022 22:39:06 +0300 Subject: [PATCH 369/524] Improve code style further --- clippy_lints/src/fn_null_check.rs | 78 +++++++++---------- .../src/transmute/transmute_null_to_fn.rs | 69 ++++++++-------- 2 files changed, 73 insertions(+), 74 deletions(-) diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs index 6eb5ddd94d7b..4f79ce6f8fec 100644 --- a/clippy_lints/src/fn_null_check.rs +++ b/clippy_lints/src/fn_null_check.rs @@ -55,50 +55,50 @@ fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for FnNullCheck { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // Catching: - // (fn_ptr as * ).is_null() - if let ExprKind::MethodCall(method_name, receiver, _, _) = expr.kind - && method_name.ident.as_str() == "is_null" - && is_fn_ptr_cast(cx, receiver) - { + match expr.kind { + ExprKind::MethodCall(method_name, receiver, _, _) + if method_name.ident.as_str() == "is_null" && is_fn_ptr_cast(cx, receiver) => + { lint_expr(cx, expr); - return; - } + }, - if let ExprKind::Binary(op, left, right) = expr.kind - && let BinOpKind::Eq = op.node - { - let to_check: &Expr<'_>; - if is_fn_ptr_cast(cx, left) { - to_check = right; - } else if is_fn_ptr_cast(cx, right) { - to_check = left; - } else { - return; - } + ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { + let to_check: &Expr<'_>; + if is_fn_ptr_cast(cx, left) { + to_check = right; + } else if is_fn_ptr_cast(cx, right) { + to_check = left; + } else { + return; + } - // Catching: - // (fn_ptr as * ) == - let c = constant(cx, cx.typeck_results(), to_check); - if let Some((Constant::RawPtr(0), _)) = c { - lint_expr(cx, expr); - return; - } + match to_check.kind { + // Catching: + // (fn_ptr as * ) == (0 as ) + ExprKind::Cast(cast_expr, _) if is_integer_literal(cast_expr, 0) => { + lint_expr(cx, expr); + }, - // Catching: - // (fn_ptr as * ) == (0 as ) - if let ExprKind::Cast(cast_expr, _) = to_check.kind && is_integer_literal(cast_expr, 0) { - lint_expr(cx, expr); - return; - } + // Catching: + // (fn_ptr as * ) == std::ptr::null() + ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::ptr_null) => { + lint_expr(cx, expr); + }, - // Catching: - // (fn_ptr as * ) == std::ptr::null() - if let ExprKind::Call(func, []) = to_check.kind && - is_path_diagnostic_item(cx, func, sym::ptr_null) - { - lint_expr(cx, expr); - } + // Catching: + // (fn_ptr as * ) == + _ if matches!( + constant(cx, cx.typeck_results(), to_check), + Some((Constant::RawPtr(0), _)) + ) => + { + lint_expr(cx, expr); + }, + + _ => {}, + } + }, + _ => {}, } } } diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 074a5d317638..79d8cb084fd5 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -13,6 +13,13 @@ const NOTE_MSG: &str = "this transmute results in undefined behavior"; const HELP_MSG: &str = "try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value"; +fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { + span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| { + diag.span_label(expr.span, NOTE_MSG); + diag.help(HELP_MSG); + }); +} + pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool { if !to_ty.is_fn() { return false; @@ -21,42 +28,34 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching: // transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); - if let ExprKind::Path(ref _qpath) = arg.kind && - let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg) - { - span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| { - diag.span_label(expr.span, NOTE_MSG); - diag.help(HELP_MSG); - }); - return true; - } - // Catching: - // `std::mem::transmute(0 as *const i32)` - if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind && is_integer_literal(inner_expr, 0) { - span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| { - diag.span_label(expr.span, NOTE_MSG); - diag.help(HELP_MSG); - }); - return true; - } + match arg.kind { + ExprKind::Path(ref _qpath) if matches!(const_eval_context.expr(arg), Some(Constant::RawPtr(0))) => { + lint_expr(cx, expr); + true + }, - // Catching: - // `std::mem::transmute(std::ptr::null::())` - if let ExprKind::Call(func1, []) = arg.kind && - is_path_diagnostic_item(cx, func1, sym::ptr_null) - { - span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| { - diag.span_label(expr.span, NOTE_MSG); - diag.help(HELP_MSG); - }); - return true; - } + // Catching: + // `std::mem::transmute(0 as *const i32)` + ExprKind::Cast(inner_expr, _cast_ty) if is_integer_literal(inner_expr, 0) => { + lint_expr(cx, expr); + true + }, - // FIXME: - // Also catch transmutations of variables which are known nulls. - // To do this, MIR const propagation seems to be the better tool. - // Whenever MIR const prop routines are more developed, this will - // become available. As of this writing (25/03/19) it is not yet. - false + // Catching: + // `std::mem::transmute(std::ptr::null::())` + ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => { + lint_expr(cx, expr); + true + }, + + _ => { + // FIXME: + // Also catch transmutations of variables which are known nulls. + // To do this, MIR const propagation seems to be the better tool. + // Whenever MIR const prop routines are more developed, this will + // become available. As of this writing (25/03/19) it is not yet. + false + }, + } } From cc985748838c64b7bf4a3be635026bfffb02088c Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sun, 18 Dec 2022 22:47:02 +0300 Subject: [PATCH 370/524] Fix comments, use `constant` instead of raw `constant_context` --- clippy_lints/src/fn_null_check.rs | 2 ++ clippy_lints/src/transmute/transmute_null_to_fn.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs index 4f79ce6f8fec..91c8c340ce28 100644 --- a/clippy_lints/src/fn_null_check.rs +++ b/clippy_lints/src/fn_null_check.rs @@ -56,6 +56,8 @@ fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for FnNullCheck { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { + // Catching: + // (fn_ptr as * ).is_null() ExprKind::MethodCall(method_name, receiver, _, _) if method_name.ident.as_str() == "is_null" && is_fn_ptr_cast(cx, receiver) => { diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 79d8cb084fd5..410c9eaaba0f 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant_context, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; use rustc_hir::{Expr, ExprKind}; @@ -25,12 +25,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t return false; } - // Catching: - // transmute over constants that resolve to `null`. - let mut const_eval_context = constant_context(cx, cx.typeck_results()); - match arg.kind { - ExprKind::Path(ref _qpath) if matches!(const_eval_context.expr(arg), Some(Constant::RawPtr(0))) => { + // Catching: + // transmute over constants that resolve to `null`. + ExprKind::Path(ref _qpath) + if matches!(constant(cx, cx.typeck_results(), arg), Some((Constant::RawPtr(0), _))) => + { lint_expr(cx, expr); true }, From 62061b8a05751c8d0075c6eeebb827b3e8e0201e Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 19 Dec 2022 09:44:56 +0100 Subject: [PATCH 371/524] Move manual_clamp to nursery --- clippy_lints/src/manual_clamp.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index bb6d628af3b5..f239736d38a4 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -35,6 +35,9 @@ declare_clippy_lint! { /// Some may consider panicking in these situations to be desirable, but it also may /// introduce panicking where there wasn't any before. /// + /// See also [the discussion in the + /// PR](https://github.com/rust-lang/rust-clippy/pull/9484#issuecomment-1278922613). + /// /// ### Examples /// ```rust /// # let (input, min, max) = (0, -2, 1); @@ -78,7 +81,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.66.0"] pub MANUAL_CLAMP, - complexity, + nursery, "using a clamp pattern instead of the clamp function" } impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); From 691df70bbcab3a22a961fe4df839857af19f8413 Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Mon, 19 Dec 2022 15:32:49 +0300 Subject: [PATCH 372/524] Inline some `const`s --- .../src/transmute/transmute_null_to_fn.rs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs index 410c9eaaba0f..e75d7f6bf1d5 100644 --- a/clippy_lints/src/transmute/transmute_null_to_fn.rs +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -8,16 +8,19 @@ use rustc_span::symbol::sym; use super::TRANSMUTE_NULL_TO_FN; -const LINT_MSG: &str = "transmuting a known null pointer into a function pointer"; -const NOTE_MSG: &str = "this transmute results in undefined behavior"; -const HELP_MSG: &str = - "try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value"; - fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { - span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| { - diag.span_label(expr.span, NOTE_MSG); - diag.help(HELP_MSG); - }); + span_lint_and_then( + cx, + TRANSMUTE_NULL_TO_FN, + expr.span, + "transmuting a known null pointer into a function pointer", + |diag| { + diag.span_label(expr.span, "this transmute results in undefined behavior"); + diag.help( + "try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value" + ); + }, + ); } pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool { From d0ac6ba0b17666431b4727345059c71cde281680 Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Mon, 19 Dec 2022 18:27:42 +0300 Subject: [PATCH 373/524] Fix overflow ICE in large_stack/const_arrays --- clippy_lints/src/large_const_arrays.rs | 6 +++--- clippy_lints/src/large_stack_arrays.rs | 6 +++--- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/crashes/ice-10044.rs | 3 +++ tests/ui/crashes/ice-10044.stderr | 10 ++++++++++ 5 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 tests/ui/crashes/ice-10044.rs create mode 100644 tests/ui/crashes/ice-10044.stderr diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 76c83ab47d09..db637dfc068d 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -34,12 +34,12 @@ declare_clippy_lint! { } pub struct LargeConstArrays { - maximum_allowed_size: u64, + maximum_allowed_size: u128, } impl LargeConstArrays { #[must_use] - pub fn new(maximum_allowed_size: u64) -> Self { + pub fn new(maximum_allowed_size: u128) -> Self { Self { maximum_allowed_size } } } @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx); if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()); - if self.maximum_allowed_size < element_count * element_size; + if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size); then { let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index 5857d81ab1f2..89ae83d48f53 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -24,12 +24,12 @@ declare_clippy_lint! { } pub struct LargeStackArrays { - maximum_allowed_size: u64, + maximum_allowed_size: u128, } impl LargeStackArrays { #[must_use] - pub fn new(maximum_allowed_size: u64) -> Self { + pub fn new(maximum_allowed_size: u128) -> Self { Self { maximum_allowed_size } } } @@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && !cx.tcx.hir().parent_iter(expr.hir_id) .any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. }))) - && self.maximum_allowed_size < element_count * element_size { + && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) { span_lint_and_help( cx, LARGE_STACK_ARRAYS, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 3e7d0028c0fb..c1589c771c46 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -333,7 +333,7 @@ define_Conf! { /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. /// /// The maximum allowed size for arrays on the stack - (array_size_threshold: u64 = 512_000), + (array_size_threshold: u128 = 512_000), /// Lint: VEC_BOX. /// /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed diff --git a/tests/ui/crashes/ice-10044.rs b/tests/ui/crashes/ice-10044.rs new file mode 100644 index 000000000000..65f38fe71188 --- /dev/null +++ b/tests/ui/crashes/ice-10044.rs @@ -0,0 +1,3 @@ +fn main() { + [0; usize::MAX]; +} diff --git a/tests/ui/crashes/ice-10044.stderr b/tests/ui/crashes/ice-10044.stderr new file mode 100644 index 000000000000..731f8265ad6c --- /dev/null +++ b/tests/ui/crashes/ice-10044.stderr @@ -0,0 +1,10 @@ +error: statement with no effect + --> $DIR/ice-10044.rs:2:5 + | +LL | [0; usize::MAX]; + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::no-effect` implied by `-D warnings` + +error: aborting due to previous error + From 30e6e855088207a3fca8f1efb516f5b5dd43d1dd Mon Sep 17 00:00:00 2001 From: dboso Date: Fri, 28 Oct 2022 20:07:17 +0000 Subject: [PATCH 374/524] add [`permissions_set_readonly_false`] #9702 --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/permissions_set_readonly_false.rs | 48 +++++++++++++++++++ tests/ui/permissions_set_readonly_false.rs | 29 +++++++++++ .../ui/permissions_set_readonly_false.stderr | 11 +++++ 6 files changed, 92 insertions(+) create mode 100644 clippy_lints/src/permissions_set_readonly_false.rs create mode 100644 tests/ui/permissions_set_readonly_false.rs create mode 100644 tests/ui/permissions_set_readonly_false.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 17ff182c7bee..52efa8db4859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4461,6 +4461,7 @@ Released 2018-09-13 [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch +[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 480a65ac70ca..980e128cba41 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -495,6 +495,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO, crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO, crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, + crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, crate::precedence::PRECEDENCE_INFO, crate::ptr::CMP_NULL_INFO, crate::ptr::INVALID_NULL_PTR_USAGE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7f7d73339e45..676ef3946bf5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -235,6 +235,7 @@ mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; mod pattern_type_mismatch; +mod permissions_set_readonly_false; mod precedence; mod ptr; mod ptr_offset_with_cast; @@ -904,6 +905,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock)); store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck)); + store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs new file mode 100644 index 000000000000..7d787ea4a4c8 --- /dev/null +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -0,0 +1,48 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::paths; +use clippy_utils::ty::match_type; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `std::fs::Permissions.set_readonly` with argument `false` + /// + /// ### Why is this bad? + /// On Unix platforms this results in the file being world writable + /// + /// ### Example + /// ```rust + /// use std::fs::File; + /// let f = File::create("foo.txt").unwrap(); + /// let metadata = f.metadata().unwrap(); + /// let mut permissions = metadata.permissions(); + /// permissions.set_readonly(false); + /// ``` + #[clippy::version = "1.66.0"] + pub PERMISSIONS_SET_READONLY_FALSE, + suspicious, + "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`" +} +declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALSE]); + +impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind + && match_type(cx, cx.typeck_results().expr_ty(receiver), &paths::PERMISSIONS) + && path.ident.name == sym!(set_readonly) + && let ExprKind::Lit(lit) = &arg.kind + && LitKind::Bool(false) == lit.node { + span_lint_and_note( + cx, + PERMISSIONS_SET_READONLY_FALSE, + expr.span, + "call to `set_readonly` with argument `false`", + None, + "on Unix platforms this results in the file being world writable", + ); + } + } +} diff --git a/tests/ui/permissions_set_readonly_false.rs b/tests/ui/permissions_set_readonly_false.rs new file mode 100644 index 000000000000..28c00d100942 --- /dev/null +++ b/tests/ui/permissions_set_readonly_false.rs @@ -0,0 +1,29 @@ +#![allow(unused)] +#![warn(clippy::permissions_set_readonly_false)] + +use std::fs::File; + +struct A; + +impl A { + pub fn set_readonly(&mut self, b: bool) {} +} + +fn set_readonly(b: bool) {} + +fn main() { + let f = File::create("foo.txt").unwrap(); + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + // lint here + permissions.set_readonly(false); + // no lint + permissions.set_readonly(true); + + let mut a = A; + // no lint here - a is not of type std::fs::Permissions + a.set_readonly(false); + + // no lint here - plain function + set_readonly(false); +} diff --git a/tests/ui/permissions_set_readonly_false.stderr b/tests/ui/permissions_set_readonly_false.stderr new file mode 100644 index 000000000000..106d2f793a44 --- /dev/null +++ b/tests/ui/permissions_set_readonly_false.stderr @@ -0,0 +1,11 @@ +error: call to `set_readonly` with argument `false` + --> $DIR/permissions_set_readonly_false.rs:19:5 + | +LL | permissions.set_readonly(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: on Unix platforms this results in the file being world writable + = note: `-D clippy::permissions-set-readonly-false` implied by `-D warnings` + +error: aborting due to previous error + From e1d3c1e8f1f2d7bd819fbaafe80de74e67ebb488 Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 17 Dec 2022 20:32:46 +0900 Subject: [PATCH 375/524] refactor: fix style --- .../src/permissions_set_readonly_false.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index 7d787ea4a4c8..6c463bd8fce0 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -34,15 +34,16 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { && match_type(cx, cx.typeck_results().expr_ty(receiver), &paths::PERMISSIONS) && path.ident.name == sym!(set_readonly) && let ExprKind::Lit(lit) = &arg.kind - && LitKind::Bool(false) == lit.node { - span_lint_and_note( - cx, - PERMISSIONS_SET_READONLY_FALSE, - expr.span, - "call to `set_readonly` with argument `false`", - None, - "on Unix platforms this results in the file being world writable", - ); + && LitKind::Bool(false) == lit.node + { + span_lint_and_note( + cx, + PERMISSIONS_SET_READONLY_FALSE, + expr.span, + "call to `set_readonly` with argument `false`", + None, + "on Unix platforms this results in the file being world writable", + ); } } } From 24444945ec96dbe0dd593ed5bda57d22464a7106 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 16 Dec 2022 03:06:21 +0000 Subject: [PATCH 376/524] Make Clippy test no longer unsound --- tests/ui/borrow_interior_mutable_const/others.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs index eefeb1decb69..7c57864245a9 100644 --- a/tests/ui/borrow_interior_mutable_const/others.rs +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -42,7 +42,7 @@ impl StaticRef { impl std::ops::Deref for StaticRef { type Target = T; - fn deref(&self) -> &'static T { + fn deref(&self) -> &T { unsafe { &*self.ptr } } } From cd3d38aa271773d26a57c521a45512cb458e62a0 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 11 Oct 2022 19:33:39 -0400 Subject: [PATCH 377/524] Use `rustc_mir_dataflow::impls::MaybeStorageLive` --- clippy_utils/src/mir/maybe_storage_live.rs | 52 ---------------------- clippy_utils/src/mir/mod.rs | 2 - clippy_utils/src/mir/possible_borrower.rs | 9 ++-- 3 files changed, 3 insertions(+), 60 deletions(-) delete mode 100644 clippy_utils/src/mir/maybe_storage_live.rs diff --git a/clippy_utils/src/mir/maybe_storage_live.rs b/clippy_utils/src/mir/maybe_storage_live.rs deleted file mode 100644 index d262b335d99d..000000000000 --- a/clippy_utils/src/mir/maybe_storage_live.rs +++ /dev/null @@ -1,52 +0,0 @@ -use rustc_index::bit_set::BitSet; -use rustc_middle::mir; -use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis}; - -/// Determines liveness of each local purely based on `StorageLive`/`Dead`. -#[derive(Copy, Clone)] -pub(super) struct MaybeStorageLive; - -impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Domain = BitSet; - const NAME: &'static str = "maybe_storage_live"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = dead - BitSet::new_empty(body.local_decls.len()) - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { - for arg in body.args_iter() { - state.insert(arg); - } - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { - type Idx = mir::Local; - - fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { - match stmt.kind { - mir::StatementKind::StorageLive(l) => trans.gen(l), - mir::StatementKind::StorageDead(l) => trans.kill(l), - _ => (), - } - } - - fn terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _loc: mir::Location, - ) { - } - - fn call_return_effect( - &self, - _trans: &mut impl GenKill, - _block: mir::BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - // Nothing to do when a call returns successfully - } -} diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 818e603f665e..26c0015e87e0 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -5,8 +5,6 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::TyCtxt; -mod maybe_storage_live; - mod possible_borrower; pub use possible_borrower::PossibleBorrowerMap; diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 25717bf3d2fe..8c695801c73f 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -1,14 +1,11 @@ -use super::{ - maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor, - transitive_relation::TransitiveRelation, -}; +use super::{possible_origin::PossibleOriginVisitor, transitive_relation::TransitiveRelation}; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::LateContext; use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; use rustc_middle::ty::{self, visit::TypeVisitor}; -use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use rustc_mir_dataflow::{impls::MaybeStorageLive, Analysis, ResultsCursor}; use std::ops::ControlFlow; /// Collects the possible borrowers of each local. @@ -182,7 +179,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive + let maybe_storage_live_result = MaybeStorageLive::new(BitSet::new_empty(mir.local_decls.len())) .into_engine(cx.tcx, mir) .pass_name("redundant_clone") .iterate_to_fixpoint() From c6477eb71188311f01f409da628fab7062697bd7 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 12 Oct 2022 04:14:42 -0400 Subject: [PATCH 378/524] Add tests --- tests/ui/needless_borrow.fixed | 13 ++++++++++++- tests/ui/needless_borrow.rs | 13 ++++++++++++- tests/ui/redundant_clone.fixed | 6 ++++++ tests/ui/redundant_clone.rs | 6 ++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 4cb7f6b687f1..e970f87e2261 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(lint_reasons)] +#![feature(custom_inner_attributes, lint_reasons, rustc_private)] #![allow( unused, clippy::uninlined_format_args, @@ -491,3 +491,14 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } + +extern crate rustc_lint; +extern crate rustc_span; + +#[allow(dead_code)] +mod span_lint { + use rustc_lint::{LateContext, Lint, LintContext}; + fn foo(cx: &LateContext<'_>, lint: &'static Lint) { + cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 9a01190ed8db..55c2738fcf27 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(lint_reasons)] +#![feature(custom_inner_attributes, lint_reasons, rustc_private)] #![allow( unused, clippy::uninlined_format_args, @@ -491,3 +491,14 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } + +extern crate rustc_lint; +extern crate rustc_span; + +#[allow(dead_code)] +mod span_lint { + use rustc_lint::{LateContext, Lint, LintContext}; + fn foo(cx: &LateContext<'_>, lint: &'static Lint) { + cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); + } +} diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 00b427450935..369ae3f7147d 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -239,3 +239,9 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +#[allow(unused, clippy::manual_retain)] +fn possible_borrower_improvements() { + let mut s = String::from("foobar"); + s = s.chars().filter(|&c| c != 'o').to_owned().collect(); +} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index f899127db8d0..430672e8b8df 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -239,3 +239,9 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +#[allow(unused, clippy::manual_retain)] +fn possible_borrower_improvements() { + let mut s = String::from("foobar"); + s = s.chars().filter(|&c| c != 'o').to_owned().collect(); +} From ed519ad746e31f64c4e9255be561785612532d37 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 12 Oct 2022 04:34:25 -0400 Subject: [PATCH 379/524] Improve `possible_borrower` --- clippy_lints/src/dereference.rs | 8 +- clippy_lints/src/redundant_clone.rs | 4 +- clippy_utils/src/mir/possible_borrower.rs | 265 ++++++++++++++-------- tests/ui/needless_borrow.fixed | 2 +- tests/ui/needless_borrow.stderr | 8 +- tests/ui/redundant_clone.fixed | 2 +- tests/ui/redundant_clone.stderr | 14 +- 7 files changed, 195 insertions(+), 108 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 7b43d8ccc67d..728941b8b3d9 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1282,10 +1282,10 @@ fn referent_used_exactly_once<'tcx>( possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); } let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; - // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is - // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of - // itself. See the comment in that method for an explanation as to why. - possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) + // If `place.local` were not included here, the `copyable_iterator::warn` test would fail. The + // reason is that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible + // borrower of itself. See the comment in that method for an explanation as to why. + possible_borrower.at_most_borrowers(cx, &[local, place.local], place.local, location) && used_exactly_once(mir, place.local).unwrap_or(false) } else { false diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index c1677fb3da1c..0e7c5cca7240 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { // `res = clone(arg)` can be turned into `res = move arg;` // if `arg` is the only borrow of `cloned` at this point. - if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) { + if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg], cloned, loc) { continue; } @@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { // StorageDead(pred_arg); // res = to_path_buf(cloned); // ``` - if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) { + if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg, cloned], local, loc) { continue; } diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 8c695801c73f..ebe7d3393097 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -1,89 +1,137 @@ -use super::{possible_origin::PossibleOriginVisitor, transitive_relation::TransitiveRelation}; +use super::possible_origin::PossibleOriginVisitor; use crate::ty::is_copy; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::LateContext; -use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; -use rustc_middle::ty::{self, visit::TypeVisitor}; -use rustc_mir_dataflow::{impls::MaybeStorageLive, Analysis, ResultsCursor}; +use rustc_middle::mir::{ + self, visit::Visitor as _, BasicBlock, Local, Location, Mutability, Statement, StatementKind, Terminator, +}; +use rustc_middle::ty::{self, visit::TypeVisitor, TyCtxt}; +use rustc_mir_dataflow::{ + fmt::DebugWithContext, impls::MaybeStorageLive, lattice::JoinSemiLattice, Analysis, AnalysisDomain, + CallReturnPlaces, ResultsCursor, +}; +use std::collections::VecDeque; use std::ops::ControlFlow; /// Collects the possible borrowers of each local. /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. #[allow(clippy::module_name_repetitions)] -struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { - possible_borrower: TransitiveRelation, +struct PossibleBorrowerAnalysis<'b, 'tcx> { + tcx: TyCtxt<'tcx>, body: &'b mir::Body<'tcx>, - cx: &'a LateContext<'tcx>, possible_origin: FxHashMap>, } -impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { - fn new( - cx: &'a LateContext<'tcx>, - body: &'b mir::Body<'tcx>, - possible_origin: FxHashMap>, - ) -> Self { +#[derive(Clone, Debug, Eq, PartialEq)] +struct PossibleBorrowerState { + map: FxIndexMap>, + domain_size: usize, +} + +impl PossibleBorrowerState { + fn new(domain_size: usize) -> Self { Self { - possible_borrower: TransitiveRelation::default(), - cx, - body, - possible_origin, + map: FxIndexMap::default(), + domain_size, } } - fn into_map( - self, - cx: &'a LateContext<'tcx>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, - ) -> PossibleBorrowerMap<'b, 'tcx> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } + #[allow(clippy::similar_names)] + fn add(&mut self, borrowed: Local, borrower: Local) { + self.map + .entry(borrowed) + .or_insert(BitSet::new_empty(self.domain_size)) + .insert(borrower); + } +} + +impl DebugWithContext for PossibleBorrowerState { + fn fmt_with(&self, _ctxt: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + <_ as std::fmt::Debug>::fmt(self, f) + } + fn fmt_diff_with(&self, _old: &Self, _ctxt: &C, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + unimplemented!() + } +} - let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); +impl JoinSemiLattice for PossibleBorrowerState { + fn join(&mut self, other: &Self) -> bool { + let mut changed = false; + for (&borrowed, borrowers) in other.map.iter() { if !borrowers.is_empty() { - map.insert(row, borrowers); + changed |= self + .map + .entry(borrowed) + .or_insert(BitSet::new_empty(self.domain_size)) + .union(borrowers); } } + changed + } +} - let bs = BitSet::new_empty(self.body.local_decls.len()); - PossibleBorrowerMap { - map, - maybe_live, - bitset: (bs.clone(), bs), +impl<'b, 'tcx> AnalysisDomain<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { + type Domain = PossibleBorrowerState; + + const NAME: &'static str = "possible_borrower"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + PossibleBorrowerState::new(body.local_decls.len()) + } + + fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _entry_set: &mut Self::Domain) {} +} + +impl<'b, 'tcx> PossibleBorrowerAnalysis<'b, 'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + body: &'b mir::Body<'tcx>, + possible_origin: FxHashMap>, + ) -> Self { + Self { + tcx, + body, + possible_origin, } } } -impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - mir::Rvalue::Ref(_, _, borrowed) => { - self.possible_borrower.add(borrowed.local, lhs); - }, - other => { - if ContainsRegion - .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) - .is_continue() - { - return; - } - rvalue_locals(other, |rhs| { - if lhs != rhs { - self.possible_borrower.add(rhs, lhs); +impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { + fn apply_call_return_effect( + &self, + _state: &mut Self::Domain, + _block: BasicBlock, + _return_places: CallReturnPlaces<'_, 'tcx>, + ) { + } + + fn apply_statement_effect(&self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location) { + if let StatementKind::Assign(box (place, rvalue)) = &statement.kind { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + state.add(borrowed.local, lhs); + }, + other => { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.tcx).ty) + .is_continue() + { + return; } - }); - }, + rvalue_locals(other, |rhs| { + if lhs != rhs { + state.add(rhs, lhs); + } + }); + }, + } } } - fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { + fn apply_terminator_effect(&self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location) { if let mir::TerminatorKind::Call { args, destination: mir::Place { local: dest, .. }, @@ -123,10 +171,10 @@ impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, for y in mutable_variables { for x in &immutable_borrowers { - self.possible_borrower.add(*x, y); + state.add(*x, y); } for x in &mutable_borrowers { - self.possible_borrower.add(*x, y); + state.add(*x, y); } } } @@ -162,73 +210,94 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { } } -/// Result of `PossibleBorrowerVisitor`. +/// Result of `PossibleBorrowerAnalysis`. #[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { - /// Mapping `Local -> its possible borrowers` - pub map: FxHashMap>, + body: &'b mir::Body<'tcx>, + possible_borrower: ResultsCursor<'b, 'tcx, PossibleBorrowerAnalysis<'b, 'tcx>>, maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, - // Caches to avoid allocation of `BitSet` on every query - pub bitset: (BitSet, BitSet), } -impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { - pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { +impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { let possible_origin = { let mut vis = PossibleOriginVisitor::new(mir); vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive::new(BitSet::new_empty(mir.local_decls.len())) + let possible_borrower = PossibleBorrowerAnalysis::new(cx.tcx, mir, possible_origin) .into_engine(cx.tcx, mir) - .pass_name("redundant_clone") + .pass_name("possible_borrower") .iterate_to_fixpoint() .into_results_cursor(mir); - let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); - vis.visit_body(mir); - vis.into_map(cx, maybe_storage_live_result) - } - - /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. - pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { - self.bounded_borrowers(borrowers, borrowers, borrowed, at) + let maybe_live = MaybeStorageLive::new(BitSet::new_empty(mir.local_decls.len())) + .into_engine(cx.tcx, mir) + .pass_name("possible_borrower") + .iterate_to_fixpoint() + .into_results_cursor(mir); + PossibleBorrowerMap { + body: mir, + possible_borrower, + maybe_live, + } } - /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` - /// but no more than `above`. - pub fn bounded_borrowers( + /// Returns true if the set of borrowers of `borrowed` living at `at` includes no more than + /// `borrowers`. + /// Notes: + /// 1. It would be nice if `PossibleBorrowerMap` could store `cx` so that `at_most_borrowers` + /// would not require it to be passed in. But a `PossibleBorrowerMap` is stored in `LintPass` + /// `Dereferencing`, which outlives any `LateContext`. + /// 2. In all current uses of `at_most_borrowers`, `borrowers` is a slice of at most two + /// elements. Thus, `borrowers.contains(...)` is effectively a constant-time operation. If + /// `at_most_borrowers`'s uses were to expand beyond this, its implementation might have to be + /// adjusted. + pub fn at_most_borrowers( &mut self, - below: &[mir::Local], - above: &[mir::Local], + cx: &LateContext<'tcx>, + borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location, ) -> bool { - self.maybe_live.seek_after_primary_effect(at); + if is_copy(cx, self.body.local_decls[borrowed].ty) { + return true; + } + + self.possible_borrower.seek_before_primary_effect(at); + self.maybe_live.seek_before_primary_effect(at); - self.bitset.0.clear(); - let maybe_live = &mut self.maybe_live; - if let Some(bitset) = self.map.get(&borrowed) { - for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { - self.bitset.0.insert(b); + let possible_borrower = &self.possible_borrower.get().map; + let maybe_live = &self.maybe_live; + + let mut queued = BitSet::new_empty(self.body.local_decls.len()); + let mut deque = VecDeque::with_capacity(self.body.local_decls.len()); + + if let Some(borrowers) = possible_borrower.get(&borrowed) { + for b in borrowers.iter() { + if queued.insert(b) { + deque.push_back(b); + } } } else { - return false; + // Nothing borrows `borrowed` at `at`. + return true; } - self.bitset.1.clear(); - for b in below { - self.bitset.1.insert(*b); - } - - if !self.bitset.0.superset(&self.bitset.1) { - return false; - } + while let Some(borrower) = deque.pop_front() { + if maybe_live.contains(borrower) && !borrowers.contains(&borrower) { + return false; + } - for b in above { - self.bitset.0.remove(*b); + if let Some(borrowers) = possible_borrower.get(&borrower) { + for b in borrowers.iter() { + if queued.insert(b) { + deque.push_back(b); + } + } + } } - self.bitset.0.is_empty() + true } pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index e970f87e2261..31e1cb6c3d7f 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -499,6 +499,6 @@ extern crate rustc_span; mod span_lint { use rustc_lint::{LateContext, Lint, LintContext}; fn foo(cx: &LateContext<'_>, lint: &'static Lint) { - cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); + cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(String::new())); } } diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index d26c317124b8..98a48d68317b 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -216,5 +216,11 @@ error: the borrowed expression implements the required traits LL | foo(&a); | ^^ help: change this to: `a` -error: aborting due to 36 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:502:85 + | +LL | cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); + | ^^^^^^^^^^^^^^ help: change this to: `String::new()` + +error: aborting due to 37 previous errors diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 369ae3f7147d..a157b6a6f9ad 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -243,5 +243,5 @@ fn false_negative_5707() { #[allow(unused, clippy::manual_retain)] fn possible_borrower_improvements() { let mut s = String::from("foobar"); - s = s.chars().filter(|&c| c != 'o').to_owned().collect(); + s = s.chars().filter(|&c| c != 'o').collect(); } diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 782590034d05..1bacc2c76af1 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -179,5 +179,17 @@ note: this value is dropped without further use LL | foo(&x.clone(), move || { | ^ -error: aborting due to 15 previous errors +error: redundant clone + --> $DIR/redundant_clone.rs:246:40 + | +LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:246:9 + | +LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors From 26df55112f754033d18d2157731ae3b4ecd41aa8 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Wed, 12 Oct 2022 04:34:39 -0400 Subject: [PATCH 380/524] Fix adjacent code --- .../src/casts/cast_slice_different_sizes.rs | 2 +- clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/large_enum_variant.rs | 2 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/loops/explicit_counter_loop.rs | 2 +- clippy_lints/src/manual_async_fn.rs | 2 +- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/methods/inefficient_to_string.rs | 2 +- clippy_lints/src/methods/iter_skip_next.rs | 2 +- clippy_lints/src/methods/option_map_unwrap_or.rs | 2 +- clippy_lints/src/methods/str_splitn.rs | 2 +- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 2 +- clippy_lints/src/needless_late_init.rs | 6 +++--- clippy_lints/src/octal_escapes.rs | 4 ++-- .../src/operators/misrefactored_assign_op.rs | 2 +- clippy_lints/src/same_name_method.rs | 4 ++-- clippy_lints/src/swap.rs | 4 ++-- .../src/transmute/transmute_undefined_repr.rs | 14 +++++++------- .../transmutes_expressible_as_ptr_casts.rs | 2 +- clippy_lints/src/transmute/useless_transmute.rs | 2 +- clippy_lints/src/types/redundant_allocation.rs | 8 ++++---- clippy_lints/src/unit_types/unit_arg.rs | 4 ++-- clippy_lints/src/write.rs | 4 ++-- clippy_utils/src/diagnostics.rs | 2 +- tests/ui/manual_retain.fixed | 2 +- tests/ui/manual_retain.rs | 2 +- 27 files changed, 43 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index e862f13e69fc..c8e54d7b8e0c 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -54,7 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv diag.span_suggestion( expr.span, - &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), + format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), sugg, rustc_errors::Applicability::HasPlaceholders, ); diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 62284235dbfe..9891955a4efe 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -377,7 +377,7 @@ fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symb call_site, &format!("`format!` in `{name}!` args"), |diag| { - diag.help(&format!( + diag.help(format!( "combine the `format!(..)` arguments with the outer `{name}!(..)` call" )); diag.help("or consider changing `format!` to `format_args!`"); diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index b18456ee5234..b8d4abdbb781 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { ); diag.span_label( def.variants[variants_size[1].ind].span, - &if variants_size[1].fields_size.is_empty() { + if variants_size[1].fields_size.is_empty() { "the second-largest variant carries no data at all".to_owned() } else { format!( diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index e88d1764a248..9eba46756299 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -361,7 +361,7 @@ fn check_for_is_empty<'tcx>( db.span_note(span, "`is_empty` defined here"); } if let Some(self_kind) = self_kind { - db.note(&output.expected_sig(self_kind)); + db.note(output.expected_sig(self_kind)); } }); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7f7d73339e45..f95b9538cef3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -335,7 +335,7 @@ pub fn read_conf(sess: &Session, path: &io::Result>) -> Conf { Ok(Some(path)) => path, Ok(None) => return Conf::default(), Err(error) => { - sess.struct_err(&format!("error finding Clippy's configuration file: {error}")) + sess.struct_err(format!("error finding Clippy's configuration file: {error}")) .emit(); return Conf::default(); }, diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 14f223481327..1953ee8a7175 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -77,7 +77,7 @@ pub(super) fn check<'tcx>( applicability, ); - diag.note(&format!( + diag.note(format!( "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" )); }, diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 075ecbe7eded..af7c05635557 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { let help = format!("make the function `async` and {ret_sugg}"); diag.span_suggestion( header_span, - &help, + help, format!("async {}{ret_snip}", &header_snip[..ret_pos]), Applicability::MachineApplicable ); diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index de166b9765f4..c795c1d9a16c 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { let test_span = expr.span.until(then.span); span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| { - diag.span_note(test_span, &format!("the {kind_word} was tested here")); + diag.span_note(test_span, format!("the {kind_word} was tested here")); multispan_sugg( diag, &format!("try using the `strip_{kind_word}` method"), diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 5c620d027160..122088f4857c 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -36,7 +36,7 @@ pub fn check( expr.span, &format!("calling `to_string` on `{arg_ty}`"), |diag| { - diag.help(&format!( + diag.help(format!( "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" )); let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index beb772100aff..279175e20c37 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -29,7 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr application = Applicability::Unspecified; diag.span_help( pat.span, - &format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), + format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), ); } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 910ee14855e2..4c6328481e43 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -84,7 +84,7 @@ pub(super) fn check<'tcx>( suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, "))); } - diag.multipart_suggestion(&format!("use `{suggest}` instead"), suggestion, applicability); + diag.multipart_suggestion(format!("use `{suggest}` instead"), suggestion, applicability); }); } } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 3c01ce1fecd3..d00708e828ea 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -167,7 +167,7 @@ fn check_manual_split_once_indirect( }; diag.span_suggestion_verbose( local.span, - &format!("try `{r}split_once`"), + format!("try `{r}split_once`"), format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), app, ); diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 0e73459ad65f..47e2e744112c 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { diag.span_suggestion( span, - &format!("use `{simplify_using}(..)` instead"), + format!("use `{simplify_using}(..)` instead"), format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), applicability, ); diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 67debe7e08af..5a9387b34cc1 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -284,7 +284,7 @@ fn check<'tcx>( diag.span_suggestion( assign.lhs_span, - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), let_snippet, Applicability::MachineApplicable, ); @@ -304,7 +304,7 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), format!("{let_snippet} = "), applicability, ); @@ -335,7 +335,7 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), format!("{let_snippet} = "), applicability, ); diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index ae0a41db918a..7376ab0c846d 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -125,7 +125,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { if is_string { "string" } else { "byte string" } ), |diag| { - diag.help(&format!( + diag.help(format!( "octal escapes are not supported, `\\0` is always a null {}", if is_string { "character" } else { "byte" } )); @@ -139,7 +139,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { // suggestion 2: unambiguous null byte diag.span_suggestion( span, - &format!( + format!( "if the null {} is intended, disambiguate using", if is_string { "character" } else { "byte" } ), diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index ae805147f07a..015f6c14e761 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -50,7 +50,7 @@ fn lint_misrefactored_assign_op( let long = format!("{snip_a} = {}", sugg::make_binop(op.into(), a, r)); diag.span_suggestion( expr.span, - &format!( + format!( "did you mean `{snip_a} = {snip_a} {} {snip_r}` or `{long}`? Consider replacing it with", op.as_str() ), diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 91326558cd8d..f35bfb0b4ed8 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { |diag| { diag.span_note( trait_method_span, - &format!("existing `{method_name}` defined here"), + format!("existing `{method_name}` defined here"), ); }, ); @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { // iterate on trait_spans? diag.span_note( trait_spans[0], - &format!("existing `{method_name}` defined here"), + format!("existing `{method_name}` defined here"), ); }, ); diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index c374529d1ea9..17e9cc5f6b7c 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -132,7 +132,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa applicability, ); if !is_xor_based { - diag.note(&format!("or maybe you should use `{sugg}::mem::replace`?")); + diag.note(format!("or maybe you should use `{sugg}::mem::replace`?")); } }, ); @@ -214,7 +214,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { Applicability::MaybeIncorrect, ); diag.note( - &format!("or maybe you should use `{sugg}::mem::replace`?") + format!("or maybe you should use `{sugg}::mem::replace`?") ); } }); diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 34642f4b122f..af0242348ac2 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -77,7 +77,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty.peel_refs() { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -91,7 +91,7 @@ pub(super) fn check<'tcx>( &format!("transmute to `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty.peel_refs() { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } }, ); @@ -119,16 +119,16 @@ pub(super) fn check<'tcx>( ), |diag| { if let Some(same_adt_did) = same_adt_did { - diag.note(&format!( + diag.note(format!( "two instances of the same generic type (`{}`) may have different layouts", cx.tcx.item_name(same_adt_did) )); } else { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } } }, @@ -146,7 +146,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -163,7 +163,7 @@ pub(super) fn check<'tcx>( &format!("transmute into `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } }, ); diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 6b444922a7cc..b79d4e915a27 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg = arg.as_ty(&to_ty.to_string()).to_string(); + let sugg = arg.as_ty(to_ty.to_string()).to_string(); diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index f919bbd5afca..871c3fadbba7 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -61,7 +61,7 @@ pub(super) fn check<'tcx>( "transmute from an integer to a pointer", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - diag.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()), Applicability::Unspecified); + diag.span_suggestion(e.span, "try", arg.as_ty(to_ty.to_string()), Applicability::Unspecified); } }, ); diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index fae5385ffc84..f9b9a66b5fa4 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ &format!("usage of `{outer_sym}<{generic_snippet}>`"), |diag| { diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability); - diag.note(&format!( + diag.note(format!( "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap" )); }, @@ -78,7 +78,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ format!("{outer_sym}<{generic_snippet}>"), applicability, ); - diag.note(&format!( + diag.note(format!( "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); }, @@ -91,10 +91,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ hir_ty.span, &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { - diag.note(&format!( + diag.note(format!( "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); - diag.help(&format!( + diag.help(format!( "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`" )); }, diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index f6d3fb00f4ee..ef9f740f7047 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -129,7 +129,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( - &format!("use {singular}unit literal{plural} instead"), + format!("use {singular}unit literal{plural} instead"), args_to_recover .iter() .map(|arg| (arg.span, "()".to_string())) @@ -142,7 +142,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp let it_or_them = if plural { "them" } else { "it" }; db.span_suggestion( expr.span, - &format!( + format!( "{or}move the expression{empty_or_s} in front of the call and replace {it_or_them} with the unit literal `()`" ), sugg, diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 6b321765bc08..df3350388817 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -377,7 +377,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c // print!("\n"), write!(f, "\n") diag.multipart_suggestion( - &format!("use `{name}ln!` instead"), + format!("use `{name}ln!` instead"), vec![(name_span, format!("{name}ln")), (format_string_span, String::new())], Applicability::MachineApplicable, ); @@ -388,7 +388,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1)); diag.multipart_suggestion( - &format!("use `{name}ln!` instead"), + format!("use `{name}ln!` instead"), vec![(name_span, format!("{name}ln")), (newline_span, String::new())], Applicability::MachineApplicable, ); diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 16b160b6fd27..812f6fe71a0a 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -17,7 +17,7 @@ use std::env; fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { - diag.help(&format!( + diag.help(format!( "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { // extract just major + minor version and ignore patch versions diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index e5ae3cf3e503..8f25fea678f1 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::manual_retain)] -#![allow(unused)] +#![allow(unused, clippy::redundant_clone)] use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::BinaryHeap; diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index 1021f15edd1e..e6b3995a689b 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::manual_retain)] -#![allow(unused)] +#![allow(unused, clippy::redundant_clone)] use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::BinaryHeap; From c7dc96155853a3919b973347277d0e9bcaaa22f0 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 13 Dec 2022 12:50:03 -0500 Subject: [PATCH 381/524] Address review comments --- clippy_utils/src/mir/possible_borrower.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index ebe7d3393097..45a85af78b43 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -11,7 +11,6 @@ use rustc_mir_dataflow::{ fmt::DebugWithContext, impls::MaybeStorageLive, lattice::JoinSemiLattice, Analysis, AnalysisDomain, CallReturnPlaces, ResultsCursor, }; -use std::collections::VecDeque; use std::ops::ControlFlow; /// Collects the possible borrowers of each local. @@ -216,6 +215,8 @@ pub struct PossibleBorrowerMap<'b, 'tcx> { body: &'b mir::Body<'tcx>, possible_borrower: ResultsCursor<'b, 'tcx, PossibleBorrowerAnalysis<'b, 'tcx>>, maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + pushed: BitSet, + stack: Vec, } impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { @@ -239,6 +240,8 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { body: mir, possible_borrower, maybe_live, + pushed: BitSet::new_empty(mir.local_decls.len()), + stack: Vec::with_capacity(mir.local_decls.len()), } } @@ -269,13 +272,13 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { let possible_borrower = &self.possible_borrower.get().map; let maybe_live = &self.maybe_live; - let mut queued = BitSet::new_empty(self.body.local_decls.len()); - let mut deque = VecDeque::with_capacity(self.body.local_decls.len()); + self.pushed.clear(); + self.stack.clear(); if let Some(borrowers) = possible_borrower.get(&borrowed) { for b in borrowers.iter() { - if queued.insert(b) { - deque.push_back(b); + if self.pushed.insert(b) { + self.stack.push(b); } } } else { @@ -283,15 +286,15 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { return true; } - while let Some(borrower) = deque.pop_front() { + while let Some(borrower) = self.stack.pop() { if maybe_live.contains(borrower) && !borrowers.contains(&borrower) { return false; } if let Some(borrowers) = possible_borrower.get(&borrower) { for b in borrowers.iter() { - if queued.insert(b) { - deque.push_back(b); + if self.pushed.insert(b) { + self.stack.push(b); } } } From 4dbd8ad34e7f6820f6e9e99531353e7ffe37b76a Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 20 Dec 2022 05:30:12 -0500 Subject: [PATCH 382/524] Address https://github.com/rust-lang/rust/pull/105659 --- clippy_utils/src/mir/possible_borrower.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 45a85af78b43..395d46e7a2f8 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -11,6 +11,7 @@ use rustc_mir_dataflow::{ fmt::DebugWithContext, impls::MaybeStorageLive, lattice::JoinSemiLattice, Analysis, AnalysisDomain, CallReturnPlaces, ResultsCursor, }; +use std::borrow::Cow; use std::ops::ControlFlow; /// Collects the possible borrowers of each local. @@ -214,7 +215,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { pub struct PossibleBorrowerMap<'b, 'tcx> { body: &'b mir::Body<'tcx>, possible_borrower: ResultsCursor<'b, 'tcx, PossibleBorrowerAnalysis<'b, 'tcx>>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'b>>, pushed: BitSet, stack: Vec, } @@ -231,7 +232,7 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { .pass_name("possible_borrower") .iterate_to_fixpoint() .into_results_cursor(mir); - let maybe_live = MaybeStorageLive::new(BitSet::new_empty(mir.local_decls.len())) + let maybe_live = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) .into_engine(cx.tcx, mir) .pass_name("possible_borrower") .iterate_to_fixpoint() From b21cc36ee92a2cf3c387b47e40faa2aa4d39fd1a Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 17 Dec 2022 21:13:02 +0900 Subject: [PATCH 383/524] hotfix: add help dialog for `PermissionExt` --- .../src/permissions_set_readonly_false.rs | 33 ++++++++++--------- .../ui/permissions_set_readonly_false.stderr | 2 ++ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index 6c463bd8fce0..e7095ec191f7 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths; use clippy_utils::ty::match_type; use rustc_ast::ast::LitKind; @@ -8,11 +8,11 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// Checks for calls to `std::fs::Permissions.set_readonly` with argument `false` + /// Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`. /// /// ### Why is this bad? - /// On Unix platforms this results in the file being world writable - /// + /// On Unix platforms this results in the file being world writable, + /// equivalent to `chmod a+w `. /// ### Example /// ```rust /// use std::fs::File; @@ -32,18 +32,21 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind && match_type(cx, cx.typeck_results().expr_ty(receiver), &paths::PERMISSIONS) - && path.ident.name == sym!(set_readonly) - && let ExprKind::Lit(lit) = &arg.kind - && LitKind::Bool(false) == lit.node + && path.ident.name == sym!(set_readonly) + && let ExprKind::Lit(lit) = &arg.kind + && LitKind::Bool(false) == lit.node { - span_lint_and_note( - cx, - PERMISSIONS_SET_READONLY_FALSE, - expr.span, - "call to `set_readonly` with argument `false`", - None, - "on Unix platforms this results in the file being world writable", - ); + span_lint_and_then( + cx, + PERMISSIONS_SET_READONLY_FALSE, + expr.span, + "call to `set_readonly` with argument `false`", + |diag| { + diag.note("on Unix platforms this results in the file being world writable"); + diag.help("you can set the desired permissions using `PermissionsExt`. For more information, see\n\ + https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html"); + } + ); } } } diff --git a/tests/ui/permissions_set_readonly_false.stderr b/tests/ui/permissions_set_readonly_false.stderr index 106d2f793a44..e7a8ee6cb19b 100644 --- a/tests/ui/permissions_set_readonly_false.stderr +++ b/tests/ui/permissions_set_readonly_false.stderr @@ -5,6 +5,8 @@ LL | permissions.set_readonly(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: on Unix platforms this results in the file being world writable + = help: you can set the desired permissions using `PermissionsExt`. For more information, see + https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html = note: `-D clippy::permissions-set-readonly-false` implied by `-D warnings` error: aborting due to previous error From b6882f6107193696e14ab21fb0ee9921a4e0b842 Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Thu, 22 Dec 2022 12:40:05 +0300 Subject: [PATCH 384/524] Fix FP in needless_return when using yeet --- clippy_lints/src/returns.rs | 8 ++- tests/ui/needless_return.fixed | 5 ++ tests/ui/needless_return.rs | 5 ++ tests/ui/needless_return.stderr | 92 ++++++++++++++++----------------- 4 files changed, 63 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 81143d7799ea..d4d506605206 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -6,7 +6,7 @@ use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, LangItem, MatchSource, PatKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; @@ -207,6 +207,12 @@ fn check_final_expr<'tcx>( match &peeled_drop_expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { + // if desugar of `do yeet`, don't lint + if let Some(inner_expr) = inner + && let ExprKind::Call(path_expr, _) = inner_expr.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind { + return; + } if cx.tcx.hir().attrs(expr.hir_id).is_empty() { let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); if !borrows { diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 4386aaec49e2..d451be1f389a 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -1,6 +1,7 @@ // run-rustfix #![feature(lint_reasons)] +#![feature(yeet_expr)] #![allow(unused)] #![allow( clippy::if_same_then_else, @@ -272,4 +273,8 @@ mod issue9416 { } } +fn issue9947() -> Result<(), String> { + do yeet "hello"; +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 666dc54b76b4..e1a1bea2c0b8 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -1,6 +1,7 @@ // run-rustfix #![feature(lint_reasons)] +#![feature(yeet_expr)] #![allow(unused)] #![allow( clippy::if_same_then_else, @@ -282,4 +283,8 @@ mod issue9416 { } } +fn issue9947() -> Result<(), String> { + do yeet "hello"; +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index a8b5d86cd558..ca2253e65863 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> $DIR/needless_return.rs:26:5 + --> $DIR/needless_return.rs:27:5 | LL | return true; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:30:5 + --> $DIR/needless_return.rs:31:5 | LL | return true; | ^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:35:9 + --> $DIR/needless_return.rs:36:9 | LL | return true; | ^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:37:9 + --> $DIR/needless_return.rs:38:9 | LL | return false; | ^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:43:17 + --> $DIR/needless_return.rs:44:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:45:13 + --> $DIR/needless_return.rs:46:13 | LL | return true; | ^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:52:9 + --> $DIR/needless_return.rs:53:9 | LL | return true; | ^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:54:16 + --> $DIR/needless_return.rs:55:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:58:5 + --> $DIR/needless_return.rs:59:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:61:21 + --> $DIR/needless_return.rs:62:21 | LL | fn test_void_fun() { | _____________________^ @@ -82,7 +82,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:66:11 + --> $DIR/needless_return.rs:67:11 | LL | if b { | ___________^ @@ -92,7 +92,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:68:13 + --> $DIR/needless_return.rs:69:13 | LL | } else { | _____________^ @@ -102,7 +102,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:76:14 + --> $DIR/needless_return.rs:77:14 | LL | _ => return, | ^^^^^^ @@ -110,7 +110,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:84:24 + --> $DIR/needless_return.rs:85:24 | LL | let _ = 42; | ________________________^ @@ -120,7 +120,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:87:14 + --> $DIR/needless_return.rs:88:14 | LL | _ => return, | ^^^^^^ @@ -128,7 +128,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:100:9 + --> $DIR/needless_return.rs:101:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:102:9 + --> $DIR/needless_return.rs:103:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:124:32 + --> $DIR/needless_return.rs:125:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ @@ -152,7 +152,7 @@ LL | bar.unwrap_or_else(|_| return) = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:128:21 + --> $DIR/needless_return.rs:129:21 | LL | let _ = || { | _____________________^ @@ -162,7 +162,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:131:20 + --> $DIR/needless_return.rs:132:20 | LL | let _ = || return; | ^^^^^^ @@ -170,7 +170,7 @@ LL | let _ = || return; = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:137:32 + --> $DIR/needless_return.rs:138:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ @@ -178,7 +178,7 @@ LL | res.unwrap_or_else(|_| return Foo) = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:146:5 + --> $DIR/needless_return.rs:147:5 | LL | return true; | ^^^^^^^^^^^ @@ -186,7 +186,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:150:5 + --> $DIR/needless_return.rs:151:5 | LL | return true; | ^^^^^^^^^^^ @@ -194,7 +194,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:155:9 + --> $DIR/needless_return.rs:156:9 | LL | return true; | ^^^^^^^^^^^ @@ -202,7 +202,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:157:9 + --> $DIR/needless_return.rs:158:9 | LL | return false; | ^^^^^^^^^^^^ @@ -210,7 +210,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:163:17 + --> $DIR/needless_return.rs:164:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -218,7 +218,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:165:13 + --> $DIR/needless_return.rs:166:13 | LL | return true; | ^^^^^^^^^^^ @@ -226,7 +226,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:172:9 + --> $DIR/needless_return.rs:173:9 | LL | return true; | ^^^^^^^^^^^ @@ -234,7 +234,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:174:16 + --> $DIR/needless_return.rs:175:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -242,7 +242,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:178:5 + --> $DIR/needless_return.rs:179:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -250,7 +250,7 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:181:33 + --> $DIR/needless_return.rs:182:33 | LL | async fn async_test_void_fun() { | _________________________________^ @@ -260,7 +260,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:186:11 + --> $DIR/needless_return.rs:187:11 | LL | if b { | ___________^ @@ -270,7 +270,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:188:13 + --> $DIR/needless_return.rs:189:13 | LL | } else { | _____________^ @@ -280,7 +280,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:196:14 + --> $DIR/needless_return.rs:197:14 | LL | _ => return, | ^^^^^^ @@ -288,7 +288,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:209:9 + --> $DIR/needless_return.rs:210:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -296,7 +296,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:211:9 + --> $DIR/needless_return.rs:212:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -304,7 +304,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:227:5 + --> $DIR/needless_return.rs:228:5 | LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -312,7 +312,7 @@ LL | return format!("Hello {}", "world!"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:238:9 + --> $DIR/needless_return.rs:239:9 | LL | return true; | ^^^^^^^^^^^ @@ -320,7 +320,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:240:9 + --> $DIR/needless_return.rs:241:9 | LL | return false; | ^^^^^^^^^^^^ @@ -328,7 +328,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:247:13 + --> $DIR/needless_return.rs:248:13 | LL | return 10; | ^^^^^^^^^ @@ -336,7 +336,7 @@ LL | return 10; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:250:13 + --> $DIR/needless_return.rs:251:13 | LL | return 100; | ^^^^^^^^^^ @@ -344,7 +344,7 @@ LL | return 100; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:258:9 + --> $DIR/needless_return.rs:259:9 | LL | return 0; | ^^^^^^^^ @@ -352,7 +352,7 @@ LL | return 0; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:265:13 + --> $DIR/needless_return.rs:266:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -360,7 +360,7 @@ LL | return *(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:267:13 + --> $DIR/needless_return.rs:268:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -368,7 +368,7 @@ LL | return !*(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:274:20 + --> $DIR/needless_return.rs:275:20 | LL | let _ = 42; | ____________________^ @@ -379,7 +379,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:281:20 + --> $DIR/needless_return.rs:282:20 | LL | let _ = 42; return; | ^^^^^^^ From 0cd9b06125c5a4ffce659bc92328946be94ccc2b Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Fri, 23 Dec 2022 00:56:50 +0300 Subject: [PATCH 385/524] Small code style adjustments --- clippy_lints/src/returns.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index d4d506605206..0da50450512c 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -210,22 +210,25 @@ fn check_final_expr<'tcx>( // if desugar of `do yeet`, don't lint if let Some(inner_expr) = inner && let ExprKind::Call(path_expr, _) = inner_expr.kind - && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind { - return; + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind + { + return; } - if cx.tcx.hir().attrs(expr.hir_id).is_empty() { - let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); - if !borrows { - // check if expr return nothing - let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { - extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) - } else { - peeled_drop_expr.span - }; - - emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement); - } + if !cx.tcx.hir().attrs(expr.hir_id).is_empty() { + return; + } + let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); + if borrows { + return; } + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement); }, ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, semi_spans.clone()); From a9358260717e55c367f16bc033990bac77cfb5a1 Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Fri, 23 Dec 2022 00:57:28 +0300 Subject: [PATCH 386/524] Fix the actual bug --- clippy_lints/src/returns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 0da50450512c..bbbd9e4989e9 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -295,7 +295,7 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { ControlFlow::Break(()) } else { - ControlFlow::Continue(Descend::from(!expr.span.from_expansion())) + ControlFlow::Continue(Descend::from(!e.span.from_expansion())) } }) .is_some() From 9ff868c4ae90f2b94d086cf8cf6302118c29f2ee Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Fri, 23 Dec 2022 01:14:07 +0300 Subject: [PATCH 387/524] test test test --- tests/ui/needless_return.fixed | 10 ++++++++++ tests/ui/needless_return.rs | 10 ++++++++++ tests/ui/needless_return.stderr | 18 +++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d451be1f389a..ab1c0e590bbc 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -277,4 +277,14 @@ fn issue9947() -> Result<(), String> { do yeet "hello"; } +// without anyhow, but triggers the same bug I believe +#[expect(clippy::useless_format)] +fn issue10051() -> Result { + if true { + Ok(format!("ok!")) + } else { + Err(format!("err!")) + } +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index e1a1bea2c0b8..abed338bb9b2 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -287,4 +287,14 @@ fn issue9947() -> Result<(), String> { do yeet "hello"; } +// without anyhow, but triggers the same bug I believe +#[expect(clippy::useless_format)] +fn issue10051() -> Result { + if true { + return Ok(format!("ok!")); + } else { + return Err(format!("err!")); + } +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index ca2253e65863..52eabf6e1370 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -386,5 +386,21 @@ LL | let _ = 42; return; | = help: remove `return` -error: aborting due to 46 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:294:9 + | +LL | return Ok(format!("ok!")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:296:9 + | +LL | return Err(format!("err!")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: aborting due to 48 previous errors From faebca37e94465042e5f657ea25ebf668a1d73a1 Mon Sep 17 00:00:00 2001 From: alexey semenyuk Date: Fri, 23 Dec 2022 14:24:23 +0300 Subject: [PATCH 388/524] Changelog fix --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52efa8db4859..964479be0ca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,8 +48,8 @@ Current stable, released 2022-12-15 [#8518](https://github.com/rust-lang/rust-clippy/pull/8518) * Moved [`implicit_saturating_sub`] to `style` (Now warn-by-default) [#9584](https://github.com/rust-lang/rust-clippy/pull/9584) -* Moved `derive_partial_eq_without_eq` to `nursery` (now allow-by-default) - [#9536](https://github.com/rust-lang/rust-clippy/pull/9536) +* Moved [`derive_partial_eq_without_eq`] to `nursery` (now allow-by-default) + [#9535](https://github.com/rust-lang/rust-clippy/pull/9535) ### Enhancements From d7b9e195c2df9e576abd2eba54f3fe6516619054 Mon Sep 17 00:00:00 2001 From: Lukas Lueg Date: Sat, 17 Dec 2022 14:16:45 +0100 Subject: [PATCH 389/524] Add size_of_ref lint Fixes #9995 --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/size_of_ref.rs | 73 ++++++++++++++++++++++++++++++ tests/ui/size_of_ref.rs | 27 +++++++++++ tests/ui/size_of_ref.stderr | 27 +++++++++++ 6 files changed, 131 insertions(+) create mode 100644 clippy_lints/src/size_of_ref.rs create mode 100644 tests/ui/size_of_ref.rs create mode 100644 tests/ui/size_of_ref.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 52efa8db4859..02f3188f8be0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4547,6 +4547,7 @@ Released 2018-09-13 [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count +[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 980e128cba41..2982460c9cfa 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -537,6 +537,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO, crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO, crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO, + crate::size_of_ref::SIZE_OF_REF_INFO, crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO, crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO, crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 676ef3946bf5..ccd89b778580 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -265,6 +265,7 @@ mod shadow; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; +mod size_of_ref; mod slow_vector_initialization; mod std_instead_of_core; mod strings; @@ -906,6 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock)); store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck)); store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); + store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs new file mode 100644 index 000000000000..3fcdb4288ce6 --- /dev/null +++ b/clippy_lints/src/size_of_ref.rs @@ -0,0 +1,73 @@ +use clippy_utils::{diagnostics::span_lint_and_help, path_def_id, ty::peel_mid_ty_refs}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for calls to `std::mem::size_of_val()` where the argument is + /// a reference to a reference. + /// + /// ### Why is this bad? + /// + /// Calling `size_of_val()` with a reference to a reference as the argument + /// yields the size of the reference-type, not the size of the value behind + /// the reference. + /// + /// ### Example + /// ```rust + /// struct Foo { + /// buffer: [u8], + /// } + /// + /// impl Foo { + /// fn size(&self) -> usize { + /// // Note that `&self` as an argument is a `&&Foo`: Because `self` + /// // is already a reference, `&self` is a double-reference. + /// // The return value of `size_of_val()` therefor is the + /// // size of the reference-type, not the size of `self`. + /// std::mem::size_of_val(&self) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct Foo { + /// buffer: [u8], + /// } + /// + /// impl Foo { + /// fn size(&self) -> usize { + /// // Correct + /// std::mem::size_of_val(self) + /// } + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub SIZE_OF_REF, + suspicious, + "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended" +} +declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); + +impl LateLintPass<'_> for SizeOfRef { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Call(path, [arg]) = expr.kind + && let Some(def_id) = path_def_id(cx, path) + && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) + && let arg_ty = cx.typeck_results().expr_ty(arg) + && peel_mid_ty_refs(arg_ty).1 > 1 + { + span_lint_and_help( + cx, + SIZE_OF_REF, + expr.span, + "argument to `std::mem::size_of_val()` is a reference to a reference", + None, + "dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type", + ); + } + } +} diff --git a/tests/ui/size_of_ref.rs b/tests/ui/size_of_ref.rs new file mode 100644 index 000000000000..1e83ab82907d --- /dev/null +++ b/tests/ui/size_of_ref.rs @@ -0,0 +1,27 @@ +#![allow(unused)] +#![warn(clippy::size_of_ref)] + +use std::mem::size_of_val; + +fn main() { + let x = 5; + let y = &x; + + size_of_val(&x); // no lint + size_of_val(y); // no lint + + size_of_val(&&x); + size_of_val(&y); +} + +struct S { + field: u32, + data: Vec, +} + +impl S { + /// Get size of object including `self`, in bytes. + pub fn size(&self) -> usize { + std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) + } +} diff --git a/tests/ui/size_of_ref.stderr b/tests/ui/size_of_ref.stderr new file mode 100644 index 000000000000..d4c13ac3290b --- /dev/null +++ b/tests/ui/size_of_ref.stderr @@ -0,0 +1,27 @@ +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:13:5 + | +LL | size_of_val(&&x); + | ^^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = note: `-D clippy::size-of-ref` implied by `-D warnings` + +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:14:5 + | +LL | size_of_val(&y); + | ^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:25:9 + | +LL | std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + +error: aborting due to 3 previous errors + From 0e5fcb7b7f13c30668637568e9eb11ce0672a5ed Mon Sep 17 00:00:00 2001 From: Ray Redondo Date: Sat, 24 Dec 2022 23:47:14 -0600 Subject: [PATCH 390/524] move mutex_atomic to clippy::restriction --- clippy_lints/src/mutex_atomic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 09cb53331763..45bb217979aa 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub MUTEX_ATOMIC, - nursery, + restriction, "using a mutex where an atomic value could be used instead" } From 12f2dea229a49a1b0d25453ba7993eb35aaadc6b Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 25 Dec 2022 01:52:57 -0500 Subject: [PATCH 391/524] `not_unsafe_ptr_arg_deref` update documentation --- clippy_lints/src/functions/mod.rs | 33 ++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 91e6ffe64479..9dbce3f889be 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -63,23 +63,40 @@ declare_clippy_lint! { /// arguments but are not marked `unsafe`. /// /// ### Why is this bad? - /// The function should probably be marked `unsafe`, since - /// for an arbitrary raw pointer, there is no way of telling for sure if it is - /// valid. + /// The function should almost definitely be marked `unsafe`, since for an + /// arbitrary raw pointer, there is no way of telling for sure if it is valid. + /// + /// In general, this lint should **never be disabled** unless it is definitely a + /// false positive (please submit an issue if so) since it breaks Rust's + /// soundness guarantees, directly exposing API users to potentially dangerous + /// program behavior. This is also true for internal APIs, as it is easy to leak + /// unsoundness. + /// + /// ### Context + /// In Rust, an `unsafe {...}` block is used to indicate that the code in that + /// section has been verified in some way that the compiler can not. For a + /// function that accepts a raw pointer then accesses the pointer's data, this is + /// generally impossible as the incoming pointer could point anywhere, valid or + /// not. So, the signature should be marked `unsafe fn`: this indicates that the + /// function's caller must provide some verification that the arguments it sends + /// are valid (and then call the function within an `unsafe` block). /// /// ### Known problems /// * It does not check functions recursively so if the pointer is passed to a /// private non-`unsafe` function which does the dereferencing, the lint won't - /// trigger. + /// trigger (false negative). /// * It only checks for arguments whose type are raw pointers, not raw pointers /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or - /// `some_argument.get_raw_ptr()`). + /// `some_argument.get_raw_ptr()`) (false negative). /// /// ### Example /// ```rust,ignore /// pub fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// + /// // this call "looks" safe but will segfault or worse! + /// // foo(invalid_ptr); /// ``` /// /// Use instead: @@ -87,6 +104,12 @@ declare_clippy_lint! { /// pub unsafe fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// + /// // this would cause a compiler error for calling without `unsafe` + /// // foo(invalid_ptr); + /// + /// // sound call if the caller knows the pointer is valid + /// unsafe { foo(valid_ptr); } /// ``` #[clippy::version = "pre 1.29.0"] pub NOT_UNSAFE_PTR_ARG_DEREF, From fae19a9a79ad275af9e9f2ee5ad15404a9a1bbf7 Mon Sep 17 00:00:00 2001 From: koka Date: Mon, 26 Dec 2022 01:51:46 +0900 Subject: [PATCH 392/524] Place default values near its definitions This patch not only improves visibility, but also fixes a potential bug. When a lint description ends with code block, the string will have three backquotes at the end. Since the current implementation prints the default value immediately after that, the markdown renderer is unable to properly close the code block. --- clippy_lints/src/utils/internal_lints/metadata_collector.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 857abe77e21f..929544cd69d5 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -558,8 +558,8 @@ impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { writeln!( f, - "* `{}`: `{}`: {} (defaults to `{}`)", - self.name, self.config_type, self.doc, self.default + "* `{}`: `{}`(defaults to `{}`): {}", + self.name, self.config_type, self.default, self.doc ) } } From 6bb6dd64d44eab37fef808ce96d3abb87e5e02b3 Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Fri, 23 Dec 2022 16:05:45 -0500 Subject: [PATCH 393/524] fix incorrect suggestion in `suboptimal_flops` There was an error when trying to negate an expression like `x - 1.0`. We used to format it as `-x - 1.0` whereas a proper negation would be `-(x - 1.0)`. Therefore, we add parentheses around the expression when it is a Binary ExprKind. We also add parentheses around multiply and divide expressions, even though this is not strictly necessary. --- clippy_lints/src/floating_point_arithmetic.rs | 2 +- tests/ui/floating_point_powi.fixed | 9 ++++ tests/ui/floating_point_powi.rs | 9 ++++ tests/ui/floating_point_powi.stderr | 44 ++++++++++++++++++- 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 0ed301964758..f95b628e6c31 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -324,7 +324,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: let maybe_neg_sugg = |expr, hir_id| { let sugg = Sugg::hir(cx, expr, ".."); if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { - format!("-{sugg}") + format!("-{}", sugg.maybe_par()) } else { sugg.to_string() } diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 884d05fed71b..8ffd4cc51379 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -14,6 +14,15 @@ fn main() { let _ = (y as f32).mul_add(y as f32, x); let _ = x.mul_add(x, y).sqrt(); let _ = y.mul_add(y, x).sqrt(); + + let _ = (x - 1.0).mul_add(x - 1.0, -y); + let _ = (x - 1.0).mul_add(x - 1.0, -y) + 3.0; + let _ = (x - 1.0).mul_add(x - 1.0, -(y + 3.0)); + let _ = (y + 1.0).mul_add(-(y + 1.0), x); + let _ = (3.0 * y).mul_add(-(3.0 * y), x); + let _ = (y + 1.0 + x).mul_add(-(y + 1.0 + x), x); + let _ = (y + 1.0 + 2.0).mul_add(-(y + 1.0 + 2.0), x); + // Cases where the lint shouldn't be applied let _ = x.powi(2); let _ = x.powi(1 + 1); diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index e6a1c895371b..9ae3455a1346 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -14,6 +14,15 @@ fn main() { let _ = x + (y as f32).powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); + + let _ = (x - 1.0).powi(2) - y; + let _ = (x - 1.0).powi(2) - y + 3.0; + let _ = (x - 1.0).powi(2) - (y + 3.0); + let _ = x - (y + 1.0).powi(2); + let _ = x - (3.0 * y).powi(2); + let _ = x - (y + 1.0 + x).powi(2); + let _ = x - (y + 1.0 + 2.0).powi(2); + // Cases where the lint shouldn't be applied let _ = x.powi(2); let _ = x.powi(1 + 1); diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index 5df0de1fef22..fdf6d088052e 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -42,5 +42,47 @@ error: multiply and add expressions can be calculated more efficiently and accur LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 7 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:18:13 + | +LL | let _ = (x - 1.0).powi(2) - y; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:19:13 + | +LL | let _ = (x - 1.0).powi(2) - y + 3.0; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:20:13 + | +LL | let _ = (x - 1.0).powi(2) - (y + 3.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -(y + 3.0))` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:21:13 + | +LL | let _ = x - (y + 1.0).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0).mul_add(-(y + 1.0), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:22:13 + | +LL | let _ = x - (3.0 * y).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(3.0 * y).mul_add(-(3.0 * y), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:23:13 + | +LL | let _ = x - (y + 1.0 + x).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0 + x).mul_add(-(y + 1.0 + x), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:24:13 + | +LL | let _ = x - (y + 1.0 + 2.0).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0 + 2.0).mul_add(-(y + 1.0 + 2.0), x)` + +error: aborting due to 14 previous errors From c617a8e01cf3ac7c4b69a473be038a80e7219371 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 28 Dec 2022 18:06:11 +0100 Subject: [PATCH 394/524] Rename `Rptr` to `Ref` in AST and HIR The name makes a lot more sense, and `ty::TyKind` calls it `Ref` already as well. --- clippy_lints/src/dereference.rs | 4 ++-- clippy_lints/src/manual_async_fn.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/mut_mut.rs | 4 ++-- clippy_lints/src/needless_arbitrary_self_type.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/ptr.rs | 10 +++++----- clippy_lints/src/redundant_static_lifetimes.rs | 2 +- clippy_lints/src/ref_option_ref.rs | 4 ++-- clippy_lints/src/transmute/transmute_ptr_to_ref.rs | 2 +- clippy_lints/src/types/mod.rs | 2 +- clippy_lints/src/types/type_complexity.rs | 2 +- clippy_lints/src/types/utils.rs | 2 +- .../src/utils/internal_lints/lint_without_lint_pass.rs | 2 +- clippy_utils/src/ast_utils.rs | 2 +- clippy_utils/src/hir_utils.rs | 4 ++-- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/sugg.rs | 4 ++-- clippy_utils/src/ty.rs | 2 +- 19 files changed, 28 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 7b43d8ccc67d..05f2b92c0370 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -969,14 +969,14 @@ fn binding_ty_auto_deref_stability<'tcx>( precedence: i8, binder_args: &'tcx List, ) -> Position { - let TyKind::Rptr(_, ty) = &ty.kind else { + let TyKind::Ref(_, ty) = &ty.kind else { return Position::Other(precedence); }; let mut ty = ty; loop { break match ty.ty.kind { - TyKind::Rptr(_, ref ref_ty) => { + TyKind::Ref(_, ref ref_ty) => { ty = ref_ty; continue; }, diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 075ecbe7eded..676a37e04f60 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -152,7 +152,7 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) let input_lifetimes: Vec = inputs .iter() .filter_map(|ty| { - if let TyKind::Rptr(lt, _) = ty.kind { + if let TyKind::Ref(lt, _) = ty.kind { Some(lt.res) } else { None diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 561e4336593b..77be61b47934 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3986,7 +3986,7 @@ impl OutType { (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), + (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), _ => false, } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index bc90e131b7f3..64d8333a093b 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { return; } - if let hir::TyKind::Rptr( + if let hir::TyKind::Ref( _, hir::MutTy { ty: pty, @@ -94,7 +94,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { }, ) = ty.kind { - if let hir::TyKind::Rptr( + if let hir::TyKind::Ref( _, hir::MutTy { mutbl: hir::Mutability::Mut, diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index f2ffac85bf40..5457eeec4eac 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -124,7 +124,7 @@ impl EarlyLintPass for NeedlessArbitrarySelfType { check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl); } }, - TyKind::Rptr(lifetime, mut_ty) => { + TyKind::Ref(lifetime, mut_ty) => { if_chain! { if let TyKind::Path(None, path) = &mut_ty.ty.kind; if let PatKind::Ident(BindingAnnotation::NONE, _, _) = p.pat.kind; diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index f9fd3645668a..75add4ee4aad 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -184,7 +184,7 @@ impl<'tcx> PassByRefOrValue { if is_copy(cx, ty) && let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()) && size <= self.ref_min_size - && let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind + && let hir::TyKind::Ref(_, MutTy { ty: decl_ty, .. }) = input.kind { if let Some(typeck) = cx.maybe_typeck_results() { // Don't lint if an unsafe pointer is created. diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index e395ff54cb15..262953042581 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -421,7 +421,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( if let ty::Ref(_, ty, mutability) = *ty.kind(); if let ty::Adt(adt, substs) = *ty.kind(); - if let TyKind::Rptr(lt, ref ty) = hir_ty.kind; + if let TyKind::Ref(lt, ref ty) = hir_ty.kind; if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind; // Check that the name as typed matches the actual name of the type. @@ -503,14 +503,14 @@ fn check_fn_args<'cx, 'tcx: 'cx>( fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) { if let FnRetTy::Return(ty) = sig.decl.output - && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) + && let Some((out, Mutability::Mut, _)) = get_ref_lm(ty) { let out_region = cx.tcx.named_region(out.hir_id); let args: Option> = sig .decl .inputs .iter() - .filter_map(get_rptr_lm) + .filter_map(get_ref_lm) .filter(|&(lt, _, _)| cx.tcx.named_region(lt.hir_id) == out_region) .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span)) .collect(); @@ -704,8 +704,8 @@ fn matches_preds<'tcx>( }) } -fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { - if let TyKind::Rptr(lt, ref m) = ty.kind { +fn get_ref_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { + if let TyKind::Ref(lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) } else { None diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 41f991a967bf..44bf824aa0e2 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -59,7 +59,7 @@ impl RedundantStaticLifetimes { } }, // This is what we are looking for ! - TyKind::Rptr(ref optional_lifetime, ref borrow_type) => { + TyKind::Ref(ref optional_lifetime, ref borrow_type) => { // Match the 'static lifetime if let Some(lifetime) = *optional_lifetime { match borrow_type.ty.kind { diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index f21b3ea6c3b0..448a32b77c03 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -39,7 +39,7 @@ declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { if_chain! { - if let TyKind::Rptr(_, ref mut_ty) = ty.kind; + if let TyKind::Ref(_, ref mut_ty) = ty.kind; if mut_ty.mutbl == Mutability::Not; if let TyKind::Path(ref qpath) = &mut_ty.ty.kind; let last = last_path_segment(qpath); @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { GenericArg::Type(inner_ty) => Some(inner_ty), _ => None, }); - if let TyKind::Rptr(_, ref inner_mut_ty) = inner_ty.kind; + if let TyKind::Ref(_, ref inner_mut_ty) = inner_ty.kind; if inner_mut_ty.mutbl == Mutability::Not; then { diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 3dde4eee6717..54ac04df1c12 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -71,7 +71,7 @@ pub(super) fn check<'tcx>( /// Gets the type `Bar` in `…::transmute`. fn get_explicit_type<'tcx>(path: &'tcx Path<'tcx>) -> Option<&'tcx hir::Ty<'tcx>> { if let GenericArg::Type(ty) = path.segments.last()?.args?.args.get(1)? - && let TyKind::Rptr(_, ty) = &ty.kind + && let TyKind::Ref(_, ty) = &ty.kind { Some(ty.ty) } else { diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 20978e81dc58..c14f056a1f2d 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -539,7 +539,7 @@ impl Types { QPath::LangItem(..) => {}, } }, - TyKind::Rptr(lt, ref mut_ty) => { + TyKind::Ref(lt, ref mut_ty) => { context.is_nested_call = true; if !borrowed_box::check(cx, hir_ty, lt, mut_ty) { self.check_ty(cx, mut_ty.ty, context); diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs index 5ca4023aa5c1..0aa50c99c169 100644 --- a/clippy_lints/src/types/type_complexity.rs +++ b/clippy_lints/src/types/type_complexity.rs @@ -44,7 +44,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { let (add_score, sub_nest) = match ty.kind { // _, &x and *x have only small overhead; don't mess with nesting level - TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0), + TyKind::Infer | TyKind::Ptr(..) | TyKind::Ref(..) => (1, 0), // the "normal" components of a type: named types, arrays/tuples TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), diff --git a/clippy_lints/src/types/utils.rs b/clippy_lints/src/types/utils.rs index 0fa75f8f0a9b..7f43b7841ff3 100644 --- a/clippy_lints/src/types/utils.rs +++ b/clippy_lints/src/types/utils.rs @@ -13,7 +13,7 @@ pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) GenericArg::Type(ty) => Some(ty), _ => None, }); - if let TyKind::Rptr(..) = ty.kind; + if let TyKind::Ref(..) = ty.kind; then { return Some(ty.span); } diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 786d9608c851..4c3b1b131fd4 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -257,7 +257,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Rptr( + if let TyKind::Ref( _, MutTy { ty: inner, diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 49e5f283db08..9d0263e93be7 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -625,7 +625,7 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool { (Slice(l), Slice(r)) => eq_ty(l, r), (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value), (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty), - (Rptr(ll, l), Rptr(rl, r)) => { + (Ref(ll, l), Ref(rl, r)) => { both(ll, rl, |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty) }, (BareFn(l), BareFn(r)) => { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 07fb6af91ba0..2bbe1a19b625 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -430,7 +430,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty), - (TyKind::Rptr(_, l_rmut), TyKind::Rptr(_, r_rmut)) => { + (TyKind::Ref(_, l_rmut), TyKind::Ref(_, r_rmut)) => { l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), @@ -950,7 +950,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, - TyKind::Rptr(lifetime, ref mut_ty) => { + TyKind::Ref(lifetime, ref mut_ty) => { self.hash_lifetime(lifetime); self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 43e2d1ec826c..d863609b6a72 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2264,7 +2264,7 @@ pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) let mut count = 0; loop { match &ty.kind { - TyKind::Rptr(_, ref_ty) => { + TyKind::Ref(_, ref_ty) => { ty = ref_ty.ty; count += 1; }, diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index b66604f33db1..a203a7afddf8 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -813,9 +813,9 @@ pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Opti let closure_body = cx.tcx.hir().body(body); // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`) // a type annotation is present if param `kind` is different from `TyKind::Infer` - let closure_arg_is_type_annotated_double_ref = if let TyKind::Rptr(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind + let closure_arg_is_type_annotated_double_ref = if let TyKind::Ref(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind { - matches!(ty.kind, TyKind::Rptr(_, MutTy { .. })) + matches!(ty.kind, TyKind::Ref(_, MutTy { .. })) } else { false }; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 2773da70d788..c8d56a3be5cf 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -496,7 +496,7 @@ pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bo /// Returns the base type for HIR references and pointers. pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { match ty.kind { - TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), + TyKind::Ptr(ref mut_ty) | TyKind::Ref(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), _ => ty, } } From 1f971866d1c102760c44179f42af2a8e99777b3c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 29 Dec 2022 14:24:44 +0100 Subject: [PATCH 395/524] Bump nightly version -> 2022-12-29 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 8e21cef32abb..9399d422036d 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-12-17" +channel = "nightly-2022-12-29" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 4ccafea92d00fe35c71fb1ab419df7b4f264d25f Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 29 Dec 2022 14:28:34 +0100 Subject: [PATCH 396/524] Merge commit '4f3ab69ea0a0908260944443c739426cc384ae1a' into clippyup --- CHANGELOG.md | 4 + .../src/casts/cast_slice_different_sizes.rs | 2 +- clippy_lints/src/declared_lints.rs | 4 + clippy_lints/src/dereference.rs | 8 +- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/fn_null_check.rs | 106 +++++++ clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/functions/mod.rs | 33 ++- clippy_lints/src/large_const_arrays.rs | 6 +- clippy_lints/src/large_enum_variant.rs | 2 +- clippy_lints/src/large_stack_arrays.rs | 6 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/lib.rs | 8 +- .../src/loops/explicit_counter_loop.rs | 2 +- clippy_lints/src/manual_async_fn.rs | 2 +- clippy_lints/src/manual_clamp.rs | 5 +- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/matches/manual_filter.rs | 17 +- .../src/matches/match_single_binding.rs | 18 +- clippy_lints/src/matches/match_wild_enum.rs | 2 +- .../src/methods/inefficient_to_string.rs | 2 +- clippy_lints/src/methods/iter_skip_next.rs | 2 +- .../src/methods/option_map_unwrap_or.rs | 2 +- clippy_lints/src/methods/str_splitn.rs | 2 +- .../src/methods/unnecessary_lazy_eval.rs | 2 +- clippy_lints/src/needless_late_init.rs | 6 +- clippy_lints/src/octal_escapes.rs | 4 +- .../src/operators/misrefactored_assign_op.rs | 2 +- .../src/permissions_set_readonly_false.rs | 52 ++++ clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/returns.rs | 8 +- clippy_lints/src/same_name_method.rs | 4 +- clippy_lints/src/size_of_ref.rs | 73 +++++ clippy_lints/src/swap.rs | 4 +- clippy_lints/src/transmute/mod.rs | 31 ++ .../src/transmute/transmute_null_to_fn.rs | 64 ++++ .../src/transmute/transmute_undefined_repr.rs | 14 +- .../transmutes_expressible_as_ptr_casts.rs | 2 +- .../src/transmute/transmuting_null.rs | 3 +- .../src/transmute/useless_transmute.rs | 2 +- .../src/types/redundant_allocation.rs | 8 +- clippy_lints/src/unit_types/unit_arg.rs | 4 +- clippy_lints/src/useless_conversion.rs | 28 +- clippy_lints/src/utils/conf.rs | 2 +- .../internal_lints/metadata_collector.rs | 4 +- clippy_lints/src/write.rs | 4 +- clippy_utils/src/consts.rs | 7 +- clippy_utils/src/diagnostics.rs | 2 +- clippy_utils/src/mir/maybe_storage_live.rs | 52 ---- clippy_utils/src/mir/mod.rs | 2 - clippy_utils/src/mir/possible_borrower.rs | 274 +++++++++++------- clippy_utils/src/sugg.rs | 1 - rust-toolchain | 2 +- tests/ui/crashes/ice-10044.rs | 3 + tests/ui/crashes/ice-10044.stderr | 10 + tests/ui/floating_point_powi.fixed | 9 + tests/ui/floating_point_powi.rs | 9 + tests/ui/floating_point_powi.stderr | 44 ++- tests/ui/fn_null_check.rs | 21 ++ tests/ui/fn_null_check.stderr | 43 +++ tests/ui/manual_filter.fixed | 41 +++ tests/ui/manual_filter.rs | 41 +++ tests/ui/manual_retain.fixed | 2 +- tests/ui/manual_retain.rs | 2 +- tests/ui/match_single_binding.fixed | 13 + tests/ui/match_single_binding.rs | 14 + tests/ui/match_single_binding.stderr | 27 +- .../match_wildcard_for_single_variants.fixed | 22 ++ .../ui/match_wildcard_for_single_variants.rs | 22 ++ .../match_wildcard_for_single_variants.stderr | 8 +- tests/ui/needless_borrow.fixed | 13 +- tests/ui/needless_borrow.rs | 13 +- tests/ui/needless_borrow.stderr | 8 +- tests/ui/needless_return.fixed | 5 + tests/ui/needless_return.rs | 5 + tests/ui/needless_return.stderr | 92 +++--- tests/ui/permissions_set_readonly_false.rs | 29 ++ .../ui/permissions_set_readonly_false.stderr | 13 + tests/ui/redundant_clone.fixed | 6 + tests/ui/redundant_clone.rs | 6 + tests/ui/redundant_clone.stderr | 14 +- tests/ui/size_of_ref.rs | 27 ++ tests/ui/size_of_ref.stderr | 27 ++ tests/ui/transmute_null_to_fn.rs | 28 ++ tests/ui/transmute_null_to_fn.stderr | 27 ++ tests/ui/useless_conversion.fixed | 73 ++++- tests/ui/useless_conversion.rs | 73 ++++- tests/ui/useless_conversion.stderr | 54 +++- tests/ui/wildcard_imports.fixed | 1 - tests/ui/wildcard_imports.rs | 1 - tests/ui/wildcard_imports.stderr | 42 +-- .../wildcard_imports_2021.edition2018.fixed | 240 +++++++++++++++ .../wildcard_imports_2021.edition2018.stderr | 132 +++++++++ .../wildcard_imports_2021.edition2021.fixed | 240 +++++++++++++++ .../wildcard_imports_2021.edition2021.stderr | 132 +++++++++ tests/ui/wildcard_imports_2021.rs | 241 +++++++++++++++ tests/ui/wildcard_imports_2021.stderr | 132 +++++++++ 97 files changed, 2555 insertions(+), 356 deletions(-) create mode 100644 clippy_lints/src/fn_null_check.rs create mode 100644 clippy_lints/src/permissions_set_readonly_false.rs create mode 100644 clippy_lints/src/size_of_ref.rs create mode 100644 clippy_lints/src/transmute/transmute_null_to_fn.rs delete mode 100644 clippy_utils/src/mir/maybe_storage_live.rs create mode 100644 tests/ui/crashes/ice-10044.rs create mode 100644 tests/ui/crashes/ice-10044.stderr create mode 100644 tests/ui/fn_null_check.rs create mode 100644 tests/ui/fn_null_check.stderr create mode 100644 tests/ui/permissions_set_readonly_false.rs create mode 100644 tests/ui/permissions_set_readonly_false.stderr create mode 100644 tests/ui/size_of_ref.rs create mode 100644 tests/ui/size_of_ref.stderr create mode 100644 tests/ui/transmute_null_to_fn.rs create mode 100644 tests/ui/transmute_null_to_fn.stderr create mode 100644 tests/ui/wildcard_imports_2021.edition2018.fixed create mode 100644 tests/ui/wildcard_imports_2021.edition2018.stderr create mode 100644 tests/ui/wildcard_imports_2021.edition2021.fixed create mode 100644 tests/ui/wildcard_imports_2021.edition2021.stderr create mode 100644 tests/ui/wildcard_imports_2021.rs create mode 100644 tests/ui/wildcard_imports_2021.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 903ee938d9d2..02f3188f8be0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4203,6 +4203,7 @@ Released 2018-09-13 [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const [`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons +[`fn_null_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_null_check [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast [`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any @@ -4460,6 +4461,7 @@ Released 2018-09-13 [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch +[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence @@ -4545,6 +4547,7 @@ Released 2018-09-13 [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count +[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive @@ -4590,6 +4593,7 @@ Released 2018-09-13 [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool [`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char [`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float +[`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn [`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes [`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr [`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index e862f13e69fc..c8e54d7b8e0c 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -54,7 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv diag.span_suggestion( expr.span, - &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), + format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), sugg, rustc_errors::Applicability::HasPlaceholders, ); diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 3cd7d1d7e722..2982460c9cfa 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -161,6 +161,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO, crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO, + crate::fn_null_check::FN_NULL_CHECK_INFO, crate::format::USELESS_FORMAT_INFO, crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO, crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO, @@ -494,6 +495,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO, crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO, crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, + crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, crate::precedence::PRECEDENCE_INFO, crate::ptr::CMP_NULL_INFO, crate::ptr::INVALID_NULL_PTR_USAGE_INFO, @@ -535,6 +537,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO, crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO, crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO, + crate::size_of_ref::SIZE_OF_REF_INFO, crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO, crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO, crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO, @@ -568,6 +571,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, + crate::transmute::TRANSMUTE_NULL_TO_FN_INFO, crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 7b43d8ccc67d..728941b8b3d9 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1282,10 +1282,10 @@ fn referent_used_exactly_once<'tcx>( possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); } let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; - // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is - // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of - // itself. See the comment in that method for an explanation as to why. - possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) + // If `place.local` were not included here, the `copyable_iterator::warn` test would fail. The + // reason is that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible + // borrower of itself. See the comment in that method for an explanation as to why. + possible_borrower.at_most_borrowers(cx, &[local, place.local], place.local, location) && used_exactly_once(mir, place.local).unwrap_or(false) } else { false diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 0ed301964758..f95b628e6c31 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -324,7 +324,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: let maybe_neg_sugg = |expr, hir_id| { let sugg = Sugg::hir(cx, expr, ".."); if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { - format!("-{sugg}") + format!("-{}", sugg.maybe_par()) } else { sugg.to_string() } diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs new file mode 100644 index 000000000000..91c8c340ce28 --- /dev/null +++ b/clippy_lints/src/fn_null_check.rs @@ -0,0 +1,106 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for comparing a function pointer to null. + /// + /// ### Why is this bad? + /// Function pointers are assumed to not be null. + /// + /// ### Example + /// ```rust,ignore + /// let fn_ptr: fn() = /* somehow obtained nullable function pointer */ + /// + /// if (fn_ptr as *const ()).is_null() { ... } + /// ``` + /// Use instead: + /// ```rust,ignore + /// let fn_ptr: Option = /* somehow obtained nullable function pointer */ + /// + /// if fn_ptr.is_none() { ... } + /// ``` + #[clippy::version = "1.67.0"] + pub FN_NULL_CHECK, + correctness, + "`fn()` type assumed to be nullable" +} +declare_lint_pass!(FnNullCheck => [FN_NULL_CHECK]); + +fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { + span_lint_and_help( + cx, + FN_NULL_CHECK, + expr.span, + "function pointer assumed to be nullable, even though it isn't", + None, + "try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value", + ); +} + +fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let ExprKind::Cast(cast_expr, cast_ty) = expr.kind + && let TyKind::Ptr(_) = cast_ty.kind + { + cx.typeck_results().expr_ty_adjusted(cast_expr).is_fn() + } else { + false + } +} + +impl<'tcx> LateLintPass<'tcx> for FnNullCheck { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + match expr.kind { + // Catching: + // (fn_ptr as * ).is_null() + ExprKind::MethodCall(method_name, receiver, _, _) + if method_name.ident.as_str() == "is_null" && is_fn_ptr_cast(cx, receiver) => + { + lint_expr(cx, expr); + }, + + ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { + let to_check: &Expr<'_>; + if is_fn_ptr_cast(cx, left) { + to_check = right; + } else if is_fn_ptr_cast(cx, right) { + to_check = left; + } else { + return; + } + + match to_check.kind { + // Catching: + // (fn_ptr as * ) == (0 as ) + ExprKind::Cast(cast_expr, _) if is_integer_literal(cast_expr, 0) => { + lint_expr(cx, expr); + }, + + // Catching: + // (fn_ptr as * ) == std::ptr::null() + ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::ptr_null) => { + lint_expr(cx, expr); + }, + + // Catching: + // (fn_ptr as * ) == + _ if matches!( + constant(cx, cx.typeck_results(), to_check), + Some((Constant::RawPtr(0), _)) + ) => + { + lint_expr(cx, expr); + }, + + _ => {}, + } + }, + _ => {}, + } + } +} diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 69f7c152fc4a..043112bbc959 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -382,7 +382,7 @@ fn check_format_in_format_args( call_site, &format!("`format!` in `{name}!` args"), |diag| { - diag.help(&format!( + diag.help(format!( "combine the `format!(..)` arguments with the outer `{name}!(..)` call" )); diag.help("or consider changing `format!` to `format_args!`"); diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 91e6ffe64479..9dbce3f889be 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -63,23 +63,40 @@ declare_clippy_lint! { /// arguments but are not marked `unsafe`. /// /// ### Why is this bad? - /// The function should probably be marked `unsafe`, since - /// for an arbitrary raw pointer, there is no way of telling for sure if it is - /// valid. + /// The function should almost definitely be marked `unsafe`, since for an + /// arbitrary raw pointer, there is no way of telling for sure if it is valid. + /// + /// In general, this lint should **never be disabled** unless it is definitely a + /// false positive (please submit an issue if so) since it breaks Rust's + /// soundness guarantees, directly exposing API users to potentially dangerous + /// program behavior. This is also true for internal APIs, as it is easy to leak + /// unsoundness. + /// + /// ### Context + /// In Rust, an `unsafe {...}` block is used to indicate that the code in that + /// section has been verified in some way that the compiler can not. For a + /// function that accepts a raw pointer then accesses the pointer's data, this is + /// generally impossible as the incoming pointer could point anywhere, valid or + /// not. So, the signature should be marked `unsafe fn`: this indicates that the + /// function's caller must provide some verification that the arguments it sends + /// are valid (and then call the function within an `unsafe` block). /// /// ### Known problems /// * It does not check functions recursively so if the pointer is passed to a /// private non-`unsafe` function which does the dereferencing, the lint won't - /// trigger. + /// trigger (false negative). /// * It only checks for arguments whose type are raw pointers, not raw pointers /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or - /// `some_argument.get_raw_ptr()`). + /// `some_argument.get_raw_ptr()`) (false negative). /// /// ### Example /// ```rust,ignore /// pub fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// + /// // this call "looks" safe but will segfault or worse! + /// // foo(invalid_ptr); /// ``` /// /// Use instead: @@ -87,6 +104,12 @@ declare_clippy_lint! { /// pub unsafe fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// + /// // this would cause a compiler error for calling without `unsafe` + /// // foo(invalid_ptr); + /// + /// // sound call if the caller knows the pointer is valid + /// unsafe { foo(valid_ptr); } /// ``` #[clippy::version = "pre 1.29.0"] pub NOT_UNSAFE_PTR_ARG_DEREF, diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 76c83ab47d09..db637dfc068d 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -34,12 +34,12 @@ declare_clippy_lint! { } pub struct LargeConstArrays { - maximum_allowed_size: u64, + maximum_allowed_size: u128, } impl LargeConstArrays { #[must_use] - pub fn new(maximum_allowed_size: u64) -> Self { + pub fn new(maximum_allowed_size: u128) -> Self { Self { maximum_allowed_size } } } @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx); if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()); - if self.maximum_allowed_size < element_count * element_size; + if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size); then { let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index b18456ee5234..b8d4abdbb781 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { ); diag.span_label( def.variants[variants_size[1].ind].span, - &if variants_size[1].fields_size.is_empty() { + if variants_size[1].fields_size.is_empty() { "the second-largest variant carries no data at all".to_owned() } else { format!( diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index 5857d81ab1f2..89ae83d48f53 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -24,12 +24,12 @@ declare_clippy_lint! { } pub struct LargeStackArrays { - maximum_allowed_size: u64, + maximum_allowed_size: u128, } impl LargeStackArrays { #[must_use] - pub fn new(maximum_allowed_size: u64) -> Self { + pub fn new(maximum_allowed_size: u128) -> Self { Self { maximum_allowed_size } } } @@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && !cx.tcx.hir().parent_iter(expr.hir_id) .any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. }))) - && self.maximum_allowed_size < element_count * element_size { + && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) { span_lint_and_help( cx, LARGE_STACK_ARRAYS, diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index e88d1764a248..9eba46756299 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -361,7 +361,7 @@ fn check_for_is_empty<'tcx>( db.span_note(span, "`is_empty` defined here"); } if let Some(self_kind) = self_kind { - db.note(&output.expected_sig(self_kind)); + db.note(output.expected_sig(self_kind)); } }); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 39850d598038..dcd8ca81ae87 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -125,6 +125,7 @@ mod explicit_write; mod fallible_impl_from; mod float_literal; mod floating_point_arithmetic; +mod fn_null_check; mod format; mod format_args; mod format_impl; @@ -234,6 +235,7 @@ mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; mod pattern_type_mismatch; +mod permissions_set_readonly_false; mod precedence; mod ptr; mod ptr_offset_with_cast; @@ -263,6 +265,7 @@ mod shadow; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; +mod size_of_ref; mod slow_vector_initialization; mod std_instead_of_core; mod strings; @@ -334,7 +337,7 @@ pub fn read_conf(sess: &Session, path: &io::Result>) -> Conf { Ok(Some(path)) => path, Ok(None) => return Conf::default(), Err(error) => { - sess.struct_err(&format!("error finding Clippy's configuration file: {error}")) + sess.struct_err(format!("error finding Clippy's configuration file: {error}")) .emit(); return Conf::default(); }, @@ -902,6 +905,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock)); + store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck)); + store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); + store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 14f223481327..1953ee8a7175 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -77,7 +77,7 @@ pub(super) fn check<'tcx>( applicability, ); - diag.note(&format!( + diag.note(format!( "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" )); }, diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 075ecbe7eded..af7c05635557 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { let help = format!("make the function `async` and {ret_sugg}"); diag.span_suggestion( header_span, - &help, + help, format!("async {}{ret_snip}", &header_snip[..ret_pos]), Applicability::MachineApplicable ); diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index bb6d628af3b5..f239736d38a4 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -35,6 +35,9 @@ declare_clippy_lint! { /// Some may consider panicking in these situations to be desirable, but it also may /// introduce panicking where there wasn't any before. /// + /// See also [the discussion in the + /// PR](https://github.com/rust-lang/rust-clippy/pull/9484#issuecomment-1278922613). + /// /// ### Examples /// ```rust /// # let (input, min, max) = (0, -2, 1); @@ -78,7 +81,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.66.0"] pub MANUAL_CLAMP, - complexity, + nursery, "using a clamp pattern instead of the clamp function" } impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index de166b9765f4..c795c1d9a16c 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { let test_span = expr.span.until(then.span); span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| { - diag.span_note(test_span, &format!("the {kind_word} was tested here")); + diag.span_note(test_span, format!("the {kind_word} was tested here")); multispan_sugg( diag, &format!("try using the `strip_{kind_word}` method"), diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index d521a529e0d6..f6bf0e7aa1ad 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; -use rustc_hir::LangItem::OptionSome; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::{sym, SyntaxContext}; @@ -25,15 +25,13 @@ fn get_cond_expr<'tcx>( if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; if let PatKind::Binding(_,target, ..) = pat.kind; - if let (then_visitor, else_visitor) - = (is_some_expr(cx, target, ctxt, then_expr), - is_some_expr(cx, target, ctxt, else_expr)); - if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` + if is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr) + || is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr); // check that one expr resolves to `Some(x)`, the other to `None` then { return Some(SomeExpr { expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), needs_unsafe_block: contains_unsafe_block(cx, expr), - needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond + needs_negated: is_none_expr(cx, then_expr) // if the `then_expr` resolves to `None`, need to negate the cond }) } }; @@ -74,6 +72,13 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: false } +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { + return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); + }; + false +} + // given the closure: `|| ` // returns `|&| ` fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String { diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 1bf8d4e96ad4..c94a1f763306 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -31,19 +31,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e }; // Do we need to add ';' to suggestion ? - match match_body.kind { - ExprKind::Block(block, _) => { - // macro + expr_ty(body) == () - if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { - snippet_body.push(';'); - } - }, - _ => { - // expr_ty(body) == () - if cx.typeck_results().expr_ty(match_body).is_unit() { - snippet_body.push(';'); - } - }, + if let ExprKind::Block(block, _) = match_body.kind { + // macro + expr_ty(body) == () + if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { + snippet_body.push(';'); + } } let mut applicability = Applicability::MaybeIncorrect; diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 7f8d124838cb..59de8c0384ba 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -30,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let mut has_non_wild = false; for arm in arms { match peel_hir_pat_refs(arm.pat).0.kind { - PatKind::Wild => wildcard_span = Some(arm.pat.span), + PatKind::Wild if arm.guard.is_none() => wildcard_span = Some(arm.pat.span), PatKind::Binding(_, _, ident, None) => { wildcard_span = Some(arm.pat.span); wildcard_ident = Some(ident); diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index d8c821bc9eee..424482859ee8 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -36,7 +36,7 @@ pub fn check( expr.span, &format!("calling `to_string` on `{arg_ty}`"), |diag| { - diag.help(&format!( + diag.help(format!( "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" )); let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index beb772100aff..279175e20c37 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -29,7 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr application = Applicability::Unspecified; diag.span_help( pat.span, - &format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), + format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), ); } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 910ee14855e2..4c6328481e43 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -84,7 +84,7 @@ pub(super) fn check<'tcx>( suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, "))); } - diag.multipart_suggestion(&format!("use `{suggest}` instead"), suggestion, applicability); + diag.multipart_suggestion(format!("use `{suggest}` instead"), suggestion, applicability); }); } } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 3c01ce1fecd3..d00708e828ea 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -167,7 +167,7 @@ fn check_manual_split_once_indirect( }; diag.span_suggestion_verbose( local.span, - &format!("try `{r}split_once`"), + format!("try `{r}split_once`"), format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), app, ); diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 0e73459ad65f..47e2e744112c 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { diag.span_suggestion( span, - &format!("use `{simplify_using}(..)` instead"), + format!("use `{simplify_using}(..)` instead"), format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), applicability, ); diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 67debe7e08af..5a9387b34cc1 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -284,7 +284,7 @@ fn check<'tcx>( diag.span_suggestion( assign.lhs_span, - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), let_snippet, Applicability::MachineApplicable, ); @@ -304,7 +304,7 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), format!("{let_snippet} = "), applicability, ); @@ -335,7 +335,7 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), format!("{let_snippet} = "), applicability, ); diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index ae0a41db918a..7376ab0c846d 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -125,7 +125,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { if is_string { "string" } else { "byte string" } ), |diag| { - diag.help(&format!( + diag.help(format!( "octal escapes are not supported, `\\0` is always a null {}", if is_string { "character" } else { "byte" } )); @@ -139,7 +139,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { // suggestion 2: unambiguous null byte diag.span_suggestion( span, - &format!( + format!( "if the null {} is intended, disambiguate using", if is_string { "character" } else { "byte" } ), diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index ae805147f07a..015f6c14e761 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -50,7 +50,7 @@ fn lint_misrefactored_assign_op( let long = format!("{snip_a} = {}", sugg::make_binop(op.into(), a, r)); diag.span_suggestion( expr.span, - &format!( + format!( "did you mean `{snip_a} = {snip_a} {} {snip_r}` or `{long}`? Consider replacing it with", op.as_str() ), diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs new file mode 100644 index 000000000000..e7095ec191f7 --- /dev/null +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -0,0 +1,52 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths; +use clippy_utils::ty::match_type; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`. + /// + /// ### Why is this bad? + /// On Unix platforms this results in the file being world writable, + /// equivalent to `chmod a+w `. + /// ### Example + /// ```rust + /// use std::fs::File; + /// let f = File::create("foo.txt").unwrap(); + /// let metadata = f.metadata().unwrap(); + /// let mut permissions = metadata.permissions(); + /// permissions.set_readonly(false); + /// ``` + #[clippy::version = "1.66.0"] + pub PERMISSIONS_SET_READONLY_FALSE, + suspicious, + "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`" +} +declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALSE]); + +impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind + && match_type(cx, cx.typeck_results().expr_ty(receiver), &paths::PERMISSIONS) + && path.ident.name == sym!(set_readonly) + && let ExprKind::Lit(lit) = &arg.kind + && LitKind::Bool(false) == lit.node + { + span_lint_and_then( + cx, + PERMISSIONS_SET_READONLY_FALSE, + expr.span, + "call to `set_readonly` with argument `false`", + |diag| { + diag.note("on Unix platforms this results in the file being world writable"); + diag.help("you can set the desired permissions using `PermissionsExt`. For more information, see\n\ + https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html"); + } + ); + } + } +} diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index c1677fb3da1c..0e7c5cca7240 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { // `res = clone(arg)` can be turned into `res = move arg;` // if `arg` is the only borrow of `cloned` at this point. - if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) { + if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg], cloned, loc) { continue; } @@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { // StorageDead(pred_arg); // res = to_path_buf(cloned); // ``` - if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) { + if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg, cloned], local, loc) { continue; } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 81143d7799ea..d4d506605206 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -6,7 +6,7 @@ use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, LangItem, MatchSource, PatKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; @@ -207,6 +207,12 @@ fn check_final_expr<'tcx>( match &peeled_drop_expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { + // if desugar of `do yeet`, don't lint + if let Some(inner_expr) = inner + && let ExprKind::Call(path_expr, _) = inner_expr.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind { + return; + } if cx.tcx.hir().attrs(expr.hir_id).is_empty() { let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); if !borrows { diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index caab5851bafc..17763128cd14 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { |diag| { diag.span_note( trait_method_span, - &format!("existing `{method_name}` defined here"), + format!("existing `{method_name}` defined here"), ); }, ); @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { // iterate on trait_spans? diag.span_note( trait_spans[0], - &format!("existing `{method_name}` defined here"), + format!("existing `{method_name}` defined here"), ); }, ); diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs new file mode 100644 index 000000000000..3fcdb4288ce6 --- /dev/null +++ b/clippy_lints/src/size_of_ref.rs @@ -0,0 +1,73 @@ +use clippy_utils::{diagnostics::span_lint_and_help, path_def_id, ty::peel_mid_ty_refs}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for calls to `std::mem::size_of_val()` where the argument is + /// a reference to a reference. + /// + /// ### Why is this bad? + /// + /// Calling `size_of_val()` with a reference to a reference as the argument + /// yields the size of the reference-type, not the size of the value behind + /// the reference. + /// + /// ### Example + /// ```rust + /// struct Foo { + /// buffer: [u8], + /// } + /// + /// impl Foo { + /// fn size(&self) -> usize { + /// // Note that `&self` as an argument is a `&&Foo`: Because `self` + /// // is already a reference, `&self` is a double-reference. + /// // The return value of `size_of_val()` therefor is the + /// // size of the reference-type, not the size of `self`. + /// std::mem::size_of_val(&self) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct Foo { + /// buffer: [u8], + /// } + /// + /// impl Foo { + /// fn size(&self) -> usize { + /// // Correct + /// std::mem::size_of_val(self) + /// } + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub SIZE_OF_REF, + suspicious, + "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended" +} +declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); + +impl LateLintPass<'_> for SizeOfRef { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Call(path, [arg]) = expr.kind + && let Some(def_id) = path_def_id(cx, path) + && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) + && let arg_ty = cx.typeck_results().expr_ty(arg) + && peel_mid_ty_refs(arg_ty).1 > 1 + { + span_lint_and_help( + cx, + SIZE_OF_REF, + expr.span, + "argument to `std::mem::size_of_val()` is a reference to a reference", + None, + "dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type", + ); + } + } +} diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index c374529d1ea9..17e9cc5f6b7c 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -132,7 +132,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa applicability, ); if !is_xor_based { - diag.note(&format!("or maybe you should use `{sugg}::mem::replace`?")); + diag.note(format!("or maybe you should use `{sugg}::mem::replace`?")); } }, ); @@ -214,7 +214,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { Applicability::MaybeIncorrect, ); diag.note( - &format!("or maybe you should use `{sugg}::mem::replace`?") + format!("or maybe you should use `{sugg}::mem::replace`?") ); } }); diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 83e651aba8e8..691d759d7739 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -3,6 +3,7 @@ mod transmute_float_to_int; mod transmute_int_to_bool; mod transmute_int_to_char; mod transmute_int_to_float; +mod transmute_null_to_fn; mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; mod transmute_ptr_to_ref; @@ -409,6 +410,34 @@ declare_clippy_lint! { "transmutes from a null pointer to a reference, which is undefined behavior" } +declare_clippy_lint! { + /// ### What it does + /// Checks for null function pointer creation through transmute. + /// + /// ### Why is this bad? + /// Creating a null function pointer is undefined behavior. + /// + /// More info: https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization + /// + /// ### Known problems + /// Not all cases can be detected at the moment of this writing. + /// For example, variables which hold a null pointer and are then fed to a `transmute` + /// call, aren't detectable yet. + /// + /// ### Example + /// ```rust + /// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null::<()>() ) }; + /// ``` + /// Use instead: + /// ```rust + /// let null_fn: Option = None; + /// ``` + #[clippy::version = "1.67.0"] + pub TRANSMUTE_NULL_TO_FN, + correctness, + "transmute results in a null function pointer, which is undefined behavior" +} + pub struct Transmute { msrv: Msrv, } @@ -428,6 +457,7 @@ impl_lint_pass!(Transmute => [ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, TRANSMUTING_NULL, + TRANSMUTE_NULL_TO_FN, ]); impl Transmute { #[must_use] @@ -461,6 +491,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmuting_null::check(cx, e, arg, to_ty) + | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs new file mode 100644 index 000000000000..e75d7f6bf1d5 --- /dev/null +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -0,0 +1,64 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::symbol::sym; + +use super::TRANSMUTE_NULL_TO_FN; + +fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { + span_lint_and_then( + cx, + TRANSMUTE_NULL_TO_FN, + expr.span, + "transmuting a known null pointer into a function pointer", + |diag| { + diag.span_label(expr.span, "this transmute results in undefined behavior"); + diag.help( + "try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value" + ); + }, + ); +} + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool { + if !to_ty.is_fn() { + return false; + } + + match arg.kind { + // Catching: + // transmute over constants that resolve to `null`. + ExprKind::Path(ref _qpath) + if matches!(constant(cx, cx.typeck_results(), arg), Some((Constant::RawPtr(0), _))) => + { + lint_expr(cx, expr); + true + }, + + // Catching: + // `std::mem::transmute(0 as *const i32)` + ExprKind::Cast(inner_expr, _cast_ty) if is_integer_literal(inner_expr, 0) => { + lint_expr(cx, expr); + true + }, + + // Catching: + // `std::mem::transmute(std::ptr::null::())` + ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => { + lint_expr(cx, expr); + true + }, + + _ => { + // FIXME: + // Also catch transmutations of variables which are known nulls. + // To do this, MIR const propagation seems to be the better tool. + // Whenever MIR const prop routines are more developed, this will + // become available. As of this writing (25/03/19) it is not yet. + false + }, + } +} diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 34642f4b122f..af0242348ac2 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -77,7 +77,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty.peel_refs() { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -91,7 +91,7 @@ pub(super) fn check<'tcx>( &format!("transmute to `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty.peel_refs() { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } }, ); @@ -119,16 +119,16 @@ pub(super) fn check<'tcx>( ), |diag| { if let Some(same_adt_did) = same_adt_did { - diag.note(&format!( + diag.note(format!( "two instances of the same generic type (`{}`) may have different layouts", cx.tcx.item_name(same_adt_did) )); } else { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } } }, @@ -146,7 +146,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -163,7 +163,7 @@ pub(super) fn check<'tcx>( &format!("transmute into `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } }, ); diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 6b444922a7cc..b79d4e915a27 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg = arg.as_ty(&to_ty.to_string()).to_string(); + let sugg = arg.as_ty(to_ty.to_string()).to_string(); diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index 19ce5ae72c24..1e407fc4138c 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -18,8 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); if let ExprKind::Path(ref _qpath) = arg.kind && - let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) && - x == 0 + let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index f919bbd5afca..871c3fadbba7 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -61,7 +61,7 @@ pub(super) fn check<'tcx>( "transmute from an integer to a pointer", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - diag.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()), Applicability::Unspecified); + diag.span_suggestion(e.span, "try", arg.as_ty(to_ty.to_string()), Applicability::Unspecified); } }, ); diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index fae5385ffc84..f9b9a66b5fa4 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -31,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ &format!("usage of `{outer_sym}<{generic_snippet}>`"), |diag| { diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability); - diag.note(&format!( + diag.note(format!( "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap" )); }, @@ -78,7 +78,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ format!("{outer_sym}<{generic_snippet}>"), applicability, ); - diag.note(&format!( + diag.note(format!( "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); }, @@ -91,10 +91,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ hir_ty.span, &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { - diag.note(&format!( + diag.note(format!( "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); - diag.help(&format!( + diag.help(format!( "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`" )); }, diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index f6d3fb00f4ee..ef9f740f7047 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -129,7 +129,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( - &format!("use {singular}unit literal{plural} instead"), + format!("use {singular}unit literal{plural} instead"), args_to_recover .iter() .map(|arg| (arg.span, "()".to_string())) @@ -142,7 +142,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp let it_or_them = if plural { "them" } else { "it" }; db.span_suggestion( expr.span, - &format!( + format!( "{or}move the expression{empty_or_s} in front of the call and replace {it_or_them} with the unit literal `()`" ), sugg, diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 3743d5d97a73..a95e7b613746 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; -use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -81,16 +81,24 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } if is_trait_method(cx, e, sym::IntoIterator) && name.ident.name == sym::into_iter { - if let Some(parent_expr) = get_parent_expr(cx, e) { - if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind { - if parent_name.ident.name != sym::into_iter { - return; - } - } + if get_parent_expr(cx, e).is_some() && + let Some(id) = path_to_local(recv) && + let Node::Pat(pat) = cx.tcx.hir().get(id) && + let PatKind::Binding(ann, ..) = pat.kind && + ann != BindingAnnotation::MUT + { + // Do not remove .into_iter() applied to a non-mutable local variable used in + // a larger expression context as it would differ in mutability. + return; } + let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); - if same_type_and_consts(a, b) { + + // If the types are identical then .into_iter() can be removed, unless the type + // implements Copy, in which case .into_iter() returns a copy of the receiver and + // cannot be safely omitted. + if same_type_and_consts(a, b) && !is_copy(cx, b) { let sugg = snippet(cx, recv.span, "").into_owned(); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 3e7d0028c0fb..c1589c771c46 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -333,7 +333,7 @@ define_Conf! { /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. /// /// The maximum allowed size for arrays on the stack - (array_size_threshold: u64 = 512_000), + (array_size_threshold: u128 = 512_000), /// Lint: VEC_BOX. /// /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 857abe77e21f..929544cd69d5 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -558,8 +558,8 @@ impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { writeln!( f, - "* `{}`: `{}`: {} (defaults to `{}`)", - self.name, self.config_type, self.doc, self.default + "* `{}`: `{}`(defaults to `{}`): {}", + self.name, self.config_type, self.default, self.doc ) } } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 6b321765bc08..df3350388817 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -377,7 +377,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c // print!("\n"), write!(f, "\n") diag.multipart_suggestion( - &format!("use `{name}ln!` instead"), + format!("use `{name}ln!` instead"), vec![(name_span, format!("{name}ln")), (format_string_span, String::new())], Applicability::MachineApplicable, ); @@ -388,7 +388,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1)); diag.multipart_suggestion( - &format!("use `{name}ln!` instead"), + format!("use `{name}ln!` instead"), vec![(name_span, format!("{name}ln")), (newline_span, String::new())], Applicability::MachineApplicable, ); diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 7a637d32babe..a67bd8d46006 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -620,12 +620,7 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) - ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( int.try_into().expect("invalid f64 bit representation"), ))), - ty::RawPtr(type_and_mut) => { - if let ty::Uint(_) = type_and_mut.ty.kind() { - return Some(Constant::RawPtr(int.assert_bits(int.size()))); - } - None - }, + ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), // FIXME: implement other conversions. _ => None, } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 16b160b6fd27..812f6fe71a0a 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -17,7 +17,7 @@ use std::env; fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { - diag.help(&format!( + diag.help(format!( "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { // extract just major + minor version and ignore patch versions diff --git a/clippy_utils/src/mir/maybe_storage_live.rs b/clippy_utils/src/mir/maybe_storage_live.rs deleted file mode 100644 index d262b335d99d..000000000000 --- a/clippy_utils/src/mir/maybe_storage_live.rs +++ /dev/null @@ -1,52 +0,0 @@ -use rustc_index::bit_set::BitSet; -use rustc_middle::mir; -use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis}; - -/// Determines liveness of each local purely based on `StorageLive`/`Dead`. -#[derive(Copy, Clone)] -pub(super) struct MaybeStorageLive; - -impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Domain = BitSet; - const NAME: &'static str = "maybe_storage_live"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = dead - BitSet::new_empty(body.local_decls.len()) - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { - for arg in body.args_iter() { - state.insert(arg); - } - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { - type Idx = mir::Local; - - fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { - match stmt.kind { - mir::StatementKind::StorageLive(l) => trans.gen(l), - mir::StatementKind::StorageDead(l) => trans.kill(l), - _ => (), - } - } - - fn terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _loc: mir::Location, - ) { - } - - fn call_return_effect( - &self, - _trans: &mut impl GenKill, - _block: mir::BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - // Nothing to do when a call returns successfully - } -} diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 818e603f665e..26c0015e87e0 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -5,8 +5,6 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::TyCtxt; -mod maybe_storage_live; - mod possible_borrower; pub use possible_borrower::PossibleBorrowerMap; diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 25717bf3d2fe..395d46e7a2f8 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -1,92 +1,137 @@ -use super::{ - maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor, - transitive_relation::TransitiveRelation, -}; +use super::possible_origin::PossibleOriginVisitor; use crate::ty::is_copy; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::LateContext; -use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; -use rustc_middle::ty::{self, visit::TypeVisitor}; -use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use rustc_middle::mir::{ + self, visit::Visitor as _, BasicBlock, Local, Location, Mutability, Statement, StatementKind, Terminator, +}; +use rustc_middle::ty::{self, visit::TypeVisitor, TyCtxt}; +use rustc_mir_dataflow::{ + fmt::DebugWithContext, impls::MaybeStorageLive, lattice::JoinSemiLattice, Analysis, AnalysisDomain, + CallReturnPlaces, ResultsCursor, +}; +use std::borrow::Cow; use std::ops::ControlFlow; /// Collects the possible borrowers of each local. /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. #[allow(clippy::module_name_repetitions)] -struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { - possible_borrower: TransitiveRelation, +struct PossibleBorrowerAnalysis<'b, 'tcx> { + tcx: TyCtxt<'tcx>, body: &'b mir::Body<'tcx>, - cx: &'a LateContext<'tcx>, possible_origin: FxHashMap>, } -impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { - fn new( - cx: &'a LateContext<'tcx>, - body: &'b mir::Body<'tcx>, - possible_origin: FxHashMap>, - ) -> Self { +#[derive(Clone, Debug, Eq, PartialEq)] +struct PossibleBorrowerState { + map: FxIndexMap>, + domain_size: usize, +} + +impl PossibleBorrowerState { + fn new(domain_size: usize) -> Self { Self { - possible_borrower: TransitiveRelation::default(), - cx, - body, - possible_origin, + map: FxIndexMap::default(), + domain_size, } } - fn into_map( - self, - cx: &'a LateContext<'tcx>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, - ) -> PossibleBorrowerMap<'b, 'tcx> { - let mut map = FxHashMap::default(); - for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { - if is_copy(cx, self.body.local_decls[row].ty) { - continue; - } + #[allow(clippy::similar_names)] + fn add(&mut self, borrowed: Local, borrower: Local) { + self.map + .entry(borrowed) + .or_insert(BitSet::new_empty(self.domain_size)) + .insert(borrower); + } +} - let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); - borrowers.remove(mir::Local::from_usize(0)); +impl DebugWithContext for PossibleBorrowerState { + fn fmt_with(&self, _ctxt: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + <_ as std::fmt::Debug>::fmt(self, f) + } + fn fmt_diff_with(&self, _old: &Self, _ctxt: &C, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + unimplemented!() + } +} + +impl JoinSemiLattice for PossibleBorrowerState { + fn join(&mut self, other: &Self) -> bool { + let mut changed = false; + for (&borrowed, borrowers) in other.map.iter() { if !borrowers.is_empty() { - map.insert(row, borrowers); + changed |= self + .map + .entry(borrowed) + .or_insert(BitSet::new_empty(self.domain_size)) + .union(borrowers); } } + changed + } +} - let bs = BitSet::new_empty(self.body.local_decls.len()); - PossibleBorrowerMap { - map, - maybe_live, - bitset: (bs.clone(), bs), +impl<'b, 'tcx> AnalysisDomain<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { + type Domain = PossibleBorrowerState; + + const NAME: &'static str = "possible_borrower"; + + fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { + PossibleBorrowerState::new(body.local_decls.len()) + } + + fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _entry_set: &mut Self::Domain) {} +} + +impl<'b, 'tcx> PossibleBorrowerAnalysis<'b, 'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + body: &'b mir::Body<'tcx>, + possible_origin: FxHashMap>, + ) -> Self { + Self { + tcx, + body, + possible_origin, } } } -impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { - fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { - let lhs = place.local; - match rvalue { - mir::Rvalue::Ref(_, _, borrowed) => { - self.possible_borrower.add(borrowed.local, lhs); - }, - other => { - if ContainsRegion - .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) - .is_continue() - { - return; - } - rvalue_locals(other, |rhs| { - if lhs != rhs { - self.possible_borrower.add(rhs, lhs); +impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { + fn apply_call_return_effect( + &self, + _state: &mut Self::Domain, + _block: BasicBlock, + _return_places: CallReturnPlaces<'_, 'tcx>, + ) { + } + + fn apply_statement_effect(&self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location) { + if let StatementKind::Assign(box (place, rvalue)) = &statement.kind { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + state.add(borrowed.local, lhs); + }, + other => { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.tcx).ty) + .is_continue() + { + return; } - }); - }, + rvalue_locals(other, |rhs| { + if lhs != rhs { + state.add(rhs, lhs); + } + }); + }, + } } } - fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { + fn apply_terminator_effect(&self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location) { if let mir::TerminatorKind::Call { args, destination: mir::Place { local: dest, .. }, @@ -126,10 +171,10 @@ impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, for y in mutable_variables { for x in &immutable_borrowers { - self.possible_borrower.add(*x, y); + state.add(*x, y); } for x in &mutable_borrowers { - self.possible_borrower.add(*x, y); + state.add(*x, y); } } } @@ -165,73 +210,98 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { } } -/// Result of `PossibleBorrowerVisitor`. +/// Result of `PossibleBorrowerAnalysis`. #[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { - /// Mapping `Local -> its possible borrowers` - pub map: FxHashMap>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, - // Caches to avoid allocation of `BitSet` on every query - pub bitset: (BitSet, BitSet), + body: &'b mir::Body<'tcx>, + possible_borrower: ResultsCursor<'b, 'tcx, PossibleBorrowerAnalysis<'b, 'tcx>>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'b>>, + pushed: BitSet, + stack: Vec, } -impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { - pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { +impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { let possible_origin = { let mut vis = PossibleOriginVisitor::new(mir); vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive + let possible_borrower = PossibleBorrowerAnalysis::new(cx.tcx, mir, possible_origin) .into_engine(cx.tcx, mir) - .pass_name("redundant_clone") + .pass_name("possible_borrower") .iterate_to_fixpoint() .into_results_cursor(mir); - let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); - vis.visit_body(mir); - vis.into_map(cx, maybe_storage_live_result) - } - - /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. - pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { - self.bounded_borrowers(borrowers, borrowers, borrowed, at) + let maybe_live = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) + .into_engine(cx.tcx, mir) + .pass_name("possible_borrower") + .iterate_to_fixpoint() + .into_results_cursor(mir); + PossibleBorrowerMap { + body: mir, + possible_borrower, + maybe_live, + pushed: BitSet::new_empty(mir.local_decls.len()), + stack: Vec::with_capacity(mir.local_decls.len()), + } } - /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` - /// but no more than `above`. - pub fn bounded_borrowers( + /// Returns true if the set of borrowers of `borrowed` living at `at` includes no more than + /// `borrowers`. + /// Notes: + /// 1. It would be nice if `PossibleBorrowerMap` could store `cx` so that `at_most_borrowers` + /// would not require it to be passed in. But a `PossibleBorrowerMap` is stored in `LintPass` + /// `Dereferencing`, which outlives any `LateContext`. + /// 2. In all current uses of `at_most_borrowers`, `borrowers` is a slice of at most two + /// elements. Thus, `borrowers.contains(...)` is effectively a constant-time operation. If + /// `at_most_borrowers`'s uses were to expand beyond this, its implementation might have to be + /// adjusted. + pub fn at_most_borrowers( &mut self, - below: &[mir::Local], - above: &[mir::Local], + cx: &LateContext<'tcx>, + borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location, ) -> bool { - self.maybe_live.seek_after_primary_effect(at); + if is_copy(cx, self.body.local_decls[borrowed].ty) { + return true; + } - self.bitset.0.clear(); - let maybe_live = &mut self.maybe_live; - if let Some(bitset) = self.map.get(&borrowed) { - for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { - self.bitset.0.insert(b); + self.possible_borrower.seek_before_primary_effect(at); + self.maybe_live.seek_before_primary_effect(at); + + let possible_borrower = &self.possible_borrower.get().map; + let maybe_live = &self.maybe_live; + + self.pushed.clear(); + self.stack.clear(); + + if let Some(borrowers) = possible_borrower.get(&borrowed) { + for b in borrowers.iter() { + if self.pushed.insert(b) { + self.stack.push(b); + } } } else { - return false; + // Nothing borrows `borrowed` at `at`. + return true; } - self.bitset.1.clear(); - for b in below { - self.bitset.1.insert(*b); - } - - if !self.bitset.0.superset(&self.bitset.1) { - return false; - } + while let Some(borrower) = self.stack.pop() { + if maybe_live.contains(borrower) && !borrowers.contains(&borrower) { + return false; + } - for b in above { - self.bitset.0.remove(*b); + if let Some(borrowers) = possible_borrower.get(&borrower) { + for b in borrowers.iter() { + if self.pushed.insert(b) { + self.stack.push(b); + } + } + } } - self.bitset.0.is_empty() + true } pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index b66604f33db1..4c4c077d771f 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -185,7 +185,6 @@ impl<'a> Sugg<'a> { ) -> Self { use rustc_ast::ast::RangeLimits; - #[expect(clippy::match_wildcard_for_single_variants)] match expr.kind { _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::AddrOf(..) diff --git a/rust-toolchain b/rust-toolchain index 8e21cef32abb..9399d422036d 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-12-17" +channel = "nightly-2022-12-29" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/ui/crashes/ice-10044.rs b/tests/ui/crashes/ice-10044.rs new file mode 100644 index 000000000000..65f38fe71188 --- /dev/null +++ b/tests/ui/crashes/ice-10044.rs @@ -0,0 +1,3 @@ +fn main() { + [0; usize::MAX]; +} diff --git a/tests/ui/crashes/ice-10044.stderr b/tests/ui/crashes/ice-10044.stderr new file mode 100644 index 000000000000..731f8265ad6c --- /dev/null +++ b/tests/ui/crashes/ice-10044.stderr @@ -0,0 +1,10 @@ +error: statement with no effect + --> $DIR/ice-10044.rs:2:5 + | +LL | [0; usize::MAX]; + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::no-effect` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 884d05fed71b..8ffd4cc51379 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -14,6 +14,15 @@ fn main() { let _ = (y as f32).mul_add(y as f32, x); let _ = x.mul_add(x, y).sqrt(); let _ = y.mul_add(y, x).sqrt(); + + let _ = (x - 1.0).mul_add(x - 1.0, -y); + let _ = (x - 1.0).mul_add(x - 1.0, -y) + 3.0; + let _ = (x - 1.0).mul_add(x - 1.0, -(y + 3.0)); + let _ = (y + 1.0).mul_add(-(y + 1.0), x); + let _ = (3.0 * y).mul_add(-(3.0 * y), x); + let _ = (y + 1.0 + x).mul_add(-(y + 1.0 + x), x); + let _ = (y + 1.0 + 2.0).mul_add(-(y + 1.0 + 2.0), x); + // Cases where the lint shouldn't be applied let _ = x.powi(2); let _ = x.powi(1 + 1); diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index e6a1c895371b..9ae3455a1346 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -14,6 +14,15 @@ fn main() { let _ = x + (y as f32).powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); + + let _ = (x - 1.0).powi(2) - y; + let _ = (x - 1.0).powi(2) - y + 3.0; + let _ = (x - 1.0).powi(2) - (y + 3.0); + let _ = x - (y + 1.0).powi(2); + let _ = x - (3.0 * y).powi(2); + let _ = x - (y + 1.0 + x).powi(2); + let _ = x - (y + 1.0 + 2.0).powi(2); + // Cases where the lint shouldn't be applied let _ = x.powi(2); let _ = x.powi(1 + 1); diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index 5df0de1fef22..fdf6d088052e 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -42,5 +42,47 @@ error: multiply and add expressions can be calculated more efficiently and accur LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 7 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:18:13 + | +LL | let _ = (x - 1.0).powi(2) - y; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:19:13 + | +LL | let _ = (x - 1.0).powi(2) - y + 3.0; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:20:13 + | +LL | let _ = (x - 1.0).powi(2) - (y + 3.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -(y + 3.0))` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:21:13 + | +LL | let _ = x - (y + 1.0).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0).mul_add(-(y + 1.0), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:22:13 + | +LL | let _ = x - (3.0 * y).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(3.0 * y).mul_add(-(3.0 * y), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:23:13 + | +LL | let _ = x - (y + 1.0 + x).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0 + x).mul_add(-(y + 1.0 + x), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:24:13 + | +LL | let _ = x - (y + 1.0 + 2.0).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0 + 2.0).mul_add(-(y + 1.0 + 2.0), x)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/fn_null_check.rs b/tests/ui/fn_null_check.rs new file mode 100644 index 000000000000..df5bc8420d57 --- /dev/null +++ b/tests/ui/fn_null_check.rs @@ -0,0 +1,21 @@ +#![allow(unused)] +#![warn(clippy::fn_null_check)] +#![allow(clippy::cmp_null)] +#![allow(clippy::ptr_eq)] +#![allow(clippy::zero_ptr)] + +pub const ZPTR: *const () = 0 as *const _; +pub const NOT_ZPTR: *const () = 1 as *const _; + +fn main() { + let fn_ptr = main; + + if (fn_ptr as *mut ()).is_null() {} + if (fn_ptr as *const u8).is_null() {} + if (fn_ptr as *const ()) == std::ptr::null() {} + if (fn_ptr as *const ()) == (0 as *const ()) {} + if (fn_ptr as *const ()) == ZPTR {} + + // no lint + if (fn_ptr as *const ()) == NOT_ZPTR {} +} diff --git a/tests/ui/fn_null_check.stderr b/tests/ui/fn_null_check.stderr new file mode 100644 index 000000000000..660dd3239792 --- /dev/null +++ b/tests/ui/fn_null_check.stderr @@ -0,0 +1,43 @@ +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:13:8 + | +LL | if (fn_ptr as *mut ()).is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + = note: `-D clippy::fn-null-check` implied by `-D warnings` + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:14:8 + | +LL | if (fn_ptr as *const u8).is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:15:8 + | +LL | if (fn_ptr as *const ()) == std::ptr::null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:16:8 + | +LL | if (fn_ptr as *const ()) == (0 as *const ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:17:8 + | +LL | if (fn_ptr as *const ()) == ZPTR {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: aborting due to 5 previous errors + diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed index 3553291b87df..ef6780dc96d9 100644 --- a/tests/ui/manual_filter.fixed +++ b/tests/ui/manual_filter.fixed @@ -116,4 +116,45 @@ fn main() { }, None => None, }; + + match Some(20) { + // Don't Lint, because `Some(3*x)` is not `None` + None => None, + Some(x) => { + if x > 0 { + Some(3 * x) + } else { + Some(x) + } + }, + }; + + // Don't lint: https://github.com/rust-lang/rust-clippy/issues/10088 + let result = if let Some(a) = maybe_some() { + if let Some(b) = maybe_some() { + Some(a + b) + } else { + Some(a) + } + } else { + None + }; + + let allowed_integers = vec![3, 4, 5, 6]; + // Don't lint, since there's a side effect in the else branch + match Some(21) { + Some(x) => { + if allowed_integers.contains(&x) { + Some(x) + } else { + println!("Invalid integer: {x:?}"); + None + } + }, + None => None, + }; +} + +fn maybe_some() -> Option { + Some(0) } diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs index aa9f90f752b1..ea0ce83172b7 100644 --- a/tests/ui/manual_filter.rs +++ b/tests/ui/manual_filter.rs @@ -240,4 +240,45 @@ fn main() { }, None => None, }; + + match Some(20) { + // Don't Lint, because `Some(3*x)` is not `None` + None => None, + Some(x) => { + if x > 0 { + Some(3 * x) + } else { + Some(x) + } + }, + }; + + // Don't lint: https://github.com/rust-lang/rust-clippy/issues/10088 + let result = if let Some(a) = maybe_some() { + if let Some(b) = maybe_some() { + Some(a + b) + } else { + Some(a) + } + } else { + None + }; + + let allowed_integers = vec![3, 4, 5, 6]; + // Don't lint, since there's a side effect in the else branch + match Some(21) { + Some(x) => { + if allowed_integers.contains(&x) { + Some(x) + } else { + println!("Invalid integer: {x:?}"); + None + } + }, + None => None, + }; +} + +fn maybe_some() -> Option { + Some(0) } diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index e5ae3cf3e503..8f25fea678f1 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::manual_retain)] -#![allow(unused)] +#![allow(unused, clippy::redundant_clone)] use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::BinaryHeap; diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index 1021f15edd1e..e6b3995a689b 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::manual_retain)] -#![allow(unused)] +#![allow(unused, clippy::redundant_clone)] use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::BinaryHeap; diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index a6e315e4773a..6cfb6661a039 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -133,3 +133,16 @@ fn issue_9575() { println!("Needs curlies"); }; } + +#[allow(dead_code)] +fn issue_9725(r: Option) { + let x = r; + match x { + Some(_) => { + println!("Some"); + }, + None => { + println!("None"); + }, + }; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index cecbd703e566..f188aeb5f2ff 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -148,3 +148,17 @@ fn issue_9575() { _ => println!("Needs curlies"), }; } + +#[allow(dead_code)] +fn issue_9725(r: Option) { + match r { + x => match x { + Some(_) => { + println!("Some"); + }, + None => { + println!("None"); + }, + }, + }; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 2b9ec7ee7026..e960d64ad2b0 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -213,5 +213,30 @@ LL + println!("Needs curlies"); LL ~ }; | -error: aborting due to 14 previous errors +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:154:5 + | +LL | / match r { +LL | | x => match x { +LL | | Some(_) => { +LL | | println!("Some"); +... | +LL | | }, +LL | | }; + | |_____^ + | +help: consider using a `let` statement + | +LL ~ let x = r; +LL + match x { +LL + Some(_) => { +LL + println!("Some"); +LL + }, +LL + None => { +LL + println!("None"); +LL + }, +LL ~ }; + | + +error: aborting due to 15 previous errors diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index e675c183ea71..fc252cdd3529 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -132,3 +132,25 @@ fn main() { } } } + +mod issue9993 { + enum Foo { + A(bool), + B, + } + + fn test() { + let _ = match Foo::A(true) { + _ if false => 0, + Foo::A(true) => 1, + Foo::A(false) => 2, + Foo::B => 3, + }; + + let _ = match Foo::B { + _ if false => 0, + Foo::A(_) => 1, + Foo::B => 2, + }; + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 38c3ffc00c71..9a5c849e6ec9 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -132,3 +132,25 @@ fn main() { } } } + +mod issue9993 { + enum Foo { + A(bool), + B, + } + + fn test() { + let _ = match Foo::A(true) { + _ if false => 0, + Foo::A(true) => 1, + Foo::A(false) => 2, + Foo::B => 3, + }; + + let _ = match Foo::B { + _ if false => 0, + Foo::A(_) => 1, + _ => 2, + }; + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 34538dea8e5f..6fa313dc9111 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -48,5 +48,11 @@ error: wildcard matches only a single variant and will also match any future add LL | _ => (), | ^ help: try this: `Color::Blue` -error: aborting due to 8 previous errors +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:153:13 + | +LL | _ => 2, + | ^ help: try this: `Foo::B` + +error: aborting due to 9 previous errors diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 4cb7f6b687f1..31e1cb6c3d7f 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(lint_reasons)] +#![feature(custom_inner_attributes, lint_reasons, rustc_private)] #![allow( unused, clippy::uninlined_format_args, @@ -491,3 +491,14 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } + +extern crate rustc_lint; +extern crate rustc_span; + +#[allow(dead_code)] +mod span_lint { + use rustc_lint::{LateContext, Lint, LintContext}; + fn foo(cx: &LateContext<'_>, lint: &'static Lint) { + cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(String::new())); + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 9a01190ed8db..55c2738fcf27 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(lint_reasons)] +#![feature(custom_inner_attributes, lint_reasons, rustc_private)] #![allow( unused, clippy::uninlined_format_args, @@ -491,3 +491,14 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } + +extern crate rustc_lint; +extern crate rustc_span; + +#[allow(dead_code)] +mod span_lint { + use rustc_lint::{LateContext, Lint, LintContext}; + fn foo(cx: &LateContext<'_>, lint: &'static Lint) { + cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); + } +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index d26c317124b8..98a48d68317b 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -216,5 +216,11 @@ error: the borrowed expression implements the required traits LL | foo(&a); | ^^ help: change this to: `a` -error: aborting due to 36 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:502:85 + | +LL | cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); + | ^^^^^^^^^^^^^^ help: change this to: `String::new()` + +error: aborting due to 37 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 4386aaec49e2..d451be1f389a 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -1,6 +1,7 @@ // run-rustfix #![feature(lint_reasons)] +#![feature(yeet_expr)] #![allow(unused)] #![allow( clippy::if_same_then_else, @@ -272,4 +273,8 @@ mod issue9416 { } } +fn issue9947() -> Result<(), String> { + do yeet "hello"; +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 666dc54b76b4..e1a1bea2c0b8 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -1,6 +1,7 @@ // run-rustfix #![feature(lint_reasons)] +#![feature(yeet_expr)] #![allow(unused)] #![allow( clippy::if_same_then_else, @@ -282,4 +283,8 @@ mod issue9416 { } } +fn issue9947() -> Result<(), String> { + do yeet "hello"; +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index a8b5d86cd558..ca2253e65863 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> $DIR/needless_return.rs:26:5 + --> $DIR/needless_return.rs:27:5 | LL | return true; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:30:5 + --> $DIR/needless_return.rs:31:5 | LL | return true; | ^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:35:9 + --> $DIR/needless_return.rs:36:9 | LL | return true; | ^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:37:9 + --> $DIR/needless_return.rs:38:9 | LL | return false; | ^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:43:17 + --> $DIR/needless_return.rs:44:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:45:13 + --> $DIR/needless_return.rs:46:13 | LL | return true; | ^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:52:9 + --> $DIR/needless_return.rs:53:9 | LL | return true; | ^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:54:16 + --> $DIR/needless_return.rs:55:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:58:5 + --> $DIR/needless_return.rs:59:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:61:21 + --> $DIR/needless_return.rs:62:21 | LL | fn test_void_fun() { | _____________________^ @@ -82,7 +82,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:66:11 + --> $DIR/needless_return.rs:67:11 | LL | if b { | ___________^ @@ -92,7 +92,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:68:13 + --> $DIR/needless_return.rs:69:13 | LL | } else { | _____________^ @@ -102,7 +102,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:76:14 + --> $DIR/needless_return.rs:77:14 | LL | _ => return, | ^^^^^^ @@ -110,7 +110,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:84:24 + --> $DIR/needless_return.rs:85:24 | LL | let _ = 42; | ________________________^ @@ -120,7 +120,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:87:14 + --> $DIR/needless_return.rs:88:14 | LL | _ => return, | ^^^^^^ @@ -128,7 +128,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:100:9 + --> $DIR/needless_return.rs:101:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:102:9 + --> $DIR/needless_return.rs:103:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:124:32 + --> $DIR/needless_return.rs:125:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ @@ -152,7 +152,7 @@ LL | bar.unwrap_or_else(|_| return) = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:128:21 + --> $DIR/needless_return.rs:129:21 | LL | let _ = || { | _____________________^ @@ -162,7 +162,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:131:20 + --> $DIR/needless_return.rs:132:20 | LL | let _ = || return; | ^^^^^^ @@ -170,7 +170,7 @@ LL | let _ = || return; = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:137:32 + --> $DIR/needless_return.rs:138:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ @@ -178,7 +178,7 @@ LL | res.unwrap_or_else(|_| return Foo) = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:146:5 + --> $DIR/needless_return.rs:147:5 | LL | return true; | ^^^^^^^^^^^ @@ -186,7 +186,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:150:5 + --> $DIR/needless_return.rs:151:5 | LL | return true; | ^^^^^^^^^^^ @@ -194,7 +194,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:155:9 + --> $DIR/needless_return.rs:156:9 | LL | return true; | ^^^^^^^^^^^ @@ -202,7 +202,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:157:9 + --> $DIR/needless_return.rs:158:9 | LL | return false; | ^^^^^^^^^^^^ @@ -210,7 +210,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:163:17 + --> $DIR/needless_return.rs:164:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -218,7 +218,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:165:13 + --> $DIR/needless_return.rs:166:13 | LL | return true; | ^^^^^^^^^^^ @@ -226,7 +226,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:172:9 + --> $DIR/needless_return.rs:173:9 | LL | return true; | ^^^^^^^^^^^ @@ -234,7 +234,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:174:16 + --> $DIR/needless_return.rs:175:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -242,7 +242,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:178:5 + --> $DIR/needless_return.rs:179:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -250,7 +250,7 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:181:33 + --> $DIR/needless_return.rs:182:33 | LL | async fn async_test_void_fun() { | _________________________________^ @@ -260,7 +260,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:186:11 + --> $DIR/needless_return.rs:187:11 | LL | if b { | ___________^ @@ -270,7 +270,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:188:13 + --> $DIR/needless_return.rs:189:13 | LL | } else { | _____________^ @@ -280,7 +280,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:196:14 + --> $DIR/needless_return.rs:197:14 | LL | _ => return, | ^^^^^^ @@ -288,7 +288,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:209:9 + --> $DIR/needless_return.rs:210:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -296,7 +296,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:211:9 + --> $DIR/needless_return.rs:212:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -304,7 +304,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:227:5 + --> $DIR/needless_return.rs:228:5 | LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -312,7 +312,7 @@ LL | return format!("Hello {}", "world!"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:238:9 + --> $DIR/needless_return.rs:239:9 | LL | return true; | ^^^^^^^^^^^ @@ -320,7 +320,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:240:9 + --> $DIR/needless_return.rs:241:9 | LL | return false; | ^^^^^^^^^^^^ @@ -328,7 +328,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:247:13 + --> $DIR/needless_return.rs:248:13 | LL | return 10; | ^^^^^^^^^ @@ -336,7 +336,7 @@ LL | return 10; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:250:13 + --> $DIR/needless_return.rs:251:13 | LL | return 100; | ^^^^^^^^^^ @@ -344,7 +344,7 @@ LL | return 100; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:258:9 + --> $DIR/needless_return.rs:259:9 | LL | return 0; | ^^^^^^^^ @@ -352,7 +352,7 @@ LL | return 0; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:265:13 + --> $DIR/needless_return.rs:266:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -360,7 +360,7 @@ LL | return *(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:267:13 + --> $DIR/needless_return.rs:268:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -368,7 +368,7 @@ LL | return !*(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:274:20 + --> $DIR/needless_return.rs:275:20 | LL | let _ = 42; | ____________________^ @@ -379,7 +379,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:281:20 + --> $DIR/needless_return.rs:282:20 | LL | let _ = 42; return; | ^^^^^^^ diff --git a/tests/ui/permissions_set_readonly_false.rs b/tests/ui/permissions_set_readonly_false.rs new file mode 100644 index 000000000000..28c00d100942 --- /dev/null +++ b/tests/ui/permissions_set_readonly_false.rs @@ -0,0 +1,29 @@ +#![allow(unused)] +#![warn(clippy::permissions_set_readonly_false)] + +use std::fs::File; + +struct A; + +impl A { + pub fn set_readonly(&mut self, b: bool) {} +} + +fn set_readonly(b: bool) {} + +fn main() { + let f = File::create("foo.txt").unwrap(); + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + // lint here + permissions.set_readonly(false); + // no lint + permissions.set_readonly(true); + + let mut a = A; + // no lint here - a is not of type std::fs::Permissions + a.set_readonly(false); + + // no lint here - plain function + set_readonly(false); +} diff --git a/tests/ui/permissions_set_readonly_false.stderr b/tests/ui/permissions_set_readonly_false.stderr new file mode 100644 index 000000000000..e7a8ee6cb19b --- /dev/null +++ b/tests/ui/permissions_set_readonly_false.stderr @@ -0,0 +1,13 @@ +error: call to `set_readonly` with argument `false` + --> $DIR/permissions_set_readonly_false.rs:19:5 + | +LL | permissions.set_readonly(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: on Unix platforms this results in the file being world writable + = help: you can set the desired permissions using `PermissionsExt`. For more information, see + https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html + = note: `-D clippy::permissions-set-readonly-false` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 00b427450935..a157b6a6f9ad 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -239,3 +239,9 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +#[allow(unused, clippy::manual_retain)] +fn possible_borrower_improvements() { + let mut s = String::from("foobar"); + s = s.chars().filter(|&c| c != 'o').collect(); +} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index f899127db8d0..430672e8b8df 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -239,3 +239,9 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } + +#[allow(unused, clippy::manual_retain)] +fn possible_borrower_improvements() { + let mut s = String::from("foobar"); + s = s.chars().filter(|&c| c != 'o').to_owned().collect(); +} diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 782590034d05..1bacc2c76af1 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -179,5 +179,17 @@ note: this value is dropped without further use LL | foo(&x.clone(), move || { | ^ -error: aborting due to 15 previous errors +error: redundant clone + --> $DIR/redundant_clone.rs:246:40 + | +LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:246:9 + | +LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors diff --git a/tests/ui/size_of_ref.rs b/tests/ui/size_of_ref.rs new file mode 100644 index 000000000000..1e83ab82907d --- /dev/null +++ b/tests/ui/size_of_ref.rs @@ -0,0 +1,27 @@ +#![allow(unused)] +#![warn(clippy::size_of_ref)] + +use std::mem::size_of_val; + +fn main() { + let x = 5; + let y = &x; + + size_of_val(&x); // no lint + size_of_val(y); // no lint + + size_of_val(&&x); + size_of_val(&y); +} + +struct S { + field: u32, + data: Vec, +} + +impl S { + /// Get size of object including `self`, in bytes. + pub fn size(&self) -> usize { + std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) + } +} diff --git a/tests/ui/size_of_ref.stderr b/tests/ui/size_of_ref.stderr new file mode 100644 index 000000000000..d4c13ac3290b --- /dev/null +++ b/tests/ui/size_of_ref.stderr @@ -0,0 +1,27 @@ +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:13:5 + | +LL | size_of_val(&&x); + | ^^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = note: `-D clippy::size-of-ref` implied by `-D warnings` + +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:14:5 + | +LL | size_of_val(&y); + | ^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:25:9 + | +LL | std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + +error: aborting due to 3 previous errors + diff --git a/tests/ui/transmute_null_to_fn.rs b/tests/ui/transmute_null_to_fn.rs new file mode 100644 index 000000000000..b3ea3d9039e0 --- /dev/null +++ b/tests/ui/transmute_null_to_fn.rs @@ -0,0 +1,28 @@ +#![allow(dead_code)] +#![warn(clippy::transmute_null_to_fn)] +#![allow(clippy::zero_ptr)] + +// Easy to lint because these only span one line. +fn one_liners() { + unsafe { + let _: fn() = std::mem::transmute(0 as *const ()); + let _: fn() = std::mem::transmute(std::ptr::null::<()>()); + } +} + +pub const ZPTR: *const usize = 0 as *const _; +pub const NOT_ZPTR: *const usize = 1 as *const _; + +fn transmute_const() { + unsafe { + // Should raise a lint. + let _: fn() = std::mem::transmute(ZPTR); + // Should NOT raise a lint. + let _: fn() = std::mem::transmute(NOT_ZPTR); + } +} + +fn main() { + one_liners(); + transmute_const(); +} diff --git a/tests/ui/transmute_null_to_fn.stderr b/tests/ui/transmute_null_to_fn.stderr new file mode 100644 index 000000000000..f0c65497d750 --- /dev/null +++ b/tests/ui/transmute_null_to_fn.stderr @@ -0,0 +1,27 @@ +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:8:23 + | +LL | let _: fn() = std::mem::transmute(0 as *const ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + = note: `-D clippy::transmute-null-to-fn` implied by `-D warnings` + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:9:23 + | +LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:19:23 + | +LL | let _: fn() = std::mem::transmute(ZPTR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: aborting due to 3 previous errors + diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 70ff08f36551..94b206d8e58f 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -33,12 +33,71 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } -fn test_issue_5833() -> Result<(), ()> { +fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); if Some("ok") == lines.into_iter().next() {} +} - Ok(()) +fn lint_into_iter_on_mutable_local_implementing_iterator_in_expr() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator_2() { + let text = "foo\r\nbar\n\nbaz\n"; + if Some("ok") == text.lines().next() {} +} + +#[allow(const_item_mutation)] +fn lint_into_iter_on_const_implementing_iterator() { + const NUMBERS: std::ops::Range = 0..10; + let _ = NUMBERS.next(); +} + +fn lint_into_iter_on_const_implementing_iterator_2() { + const NUMBERS: std::ops::Range = 0..10; + let mut n = NUMBERS; + n.next(); +} + +#[derive(Clone, Copy)] +struct CopiableCounter { + counter: u32, +} + +impl Iterator for CopiableCounter { + type Item = u32; + + fn next(&mut self) -> Option { + self.counter = self.counter.wrapping_add(1); + Some(self.counter) + } +} + +fn dont_lint_into_iter_on_copy_iter() { + let mut c = CopiableCounter { counter: 0 }; + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.next(), Some(1)); + assert_eq!(c.next(), Some(2)); +} + +fn dont_lint_into_iter_on_static_copy_iter() { + static mut C: CopiableCounter = CopiableCounter { counter: 0 }; + unsafe { + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.next(), Some(1)); + assert_eq!(C.next(), Some(2)); + } } fn main() { @@ -46,7 +105,15 @@ fn main() { test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); - test_issue_5833().unwrap(); + + dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_expr_implementing_iterator(); + lint_into_iter_on_expr_implementing_iterator_2(); + lint_into_iter_on_const_implementing_iterator(); + lint_into_iter_on_const_implementing_iterator_2(); + dont_lint_into_iter_on_copy_iter(); + dont_lint_into_iter_on_static_copy_iter(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index f2444a8f436b..c7ae927941bf 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -33,12 +33,71 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } -fn test_issue_5833() -> Result<(), ()> { +fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); if Some("ok") == lines.into_iter().next() {} +} - Ok(()) +fn lint_into_iter_on_mutable_local_implementing_iterator_in_expr() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines().into_iter(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator_2() { + let text = "foo\r\nbar\n\nbaz\n"; + if Some("ok") == text.lines().into_iter().next() {} +} + +#[allow(const_item_mutation)] +fn lint_into_iter_on_const_implementing_iterator() { + const NUMBERS: std::ops::Range = 0..10; + let _ = NUMBERS.into_iter().next(); +} + +fn lint_into_iter_on_const_implementing_iterator_2() { + const NUMBERS: std::ops::Range = 0..10; + let mut n = NUMBERS.into_iter(); + n.next(); +} + +#[derive(Clone, Copy)] +struct CopiableCounter { + counter: u32, +} + +impl Iterator for CopiableCounter { + type Item = u32; + + fn next(&mut self) -> Option { + self.counter = self.counter.wrapping_add(1); + Some(self.counter) + } +} + +fn dont_lint_into_iter_on_copy_iter() { + let mut c = CopiableCounter { counter: 0 }; + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.next(), Some(1)); + assert_eq!(c.next(), Some(2)); +} + +fn dont_lint_into_iter_on_static_copy_iter() { + static mut C: CopiableCounter = CopiableCounter { counter: 0 }; + unsafe { + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.next(), Some(1)); + assert_eq!(C.next(), Some(2)); + } } fn main() { @@ -46,7 +105,15 @@ fn main() { test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); - test_issue_5833().unwrap(); + + dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_expr_implementing_iterator(); + lint_into_iter_on_expr_implementing_iterator_2(); + lint_into_iter_on_const_implementing_iterator(); + lint_into_iter_on_const_implementing_iterator_2(); + dont_lint_into_iter_on_copy_iter(); + dont_lint_into_iter_on_static_copy_iter(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 65ee3807fa9d..be067c6843ac 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -22,71 +22,101 @@ error: useless conversion to the same type: `i32` LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:45:22 + | +LL | if Some("ok") == lines.into_iter().next() {} + | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `lines` + +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:50:21 + | +LL | let mut lines = text.lines().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` + +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:56:22 + | +LL | if Some("ok") == text.lines().into_iter().next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` + +error: useless conversion to the same type: `std::ops::Range` + --> $DIR/useless_conversion.rs:62:13 + | +LL | let _ = NUMBERS.into_iter().next(); + | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` + +error: useless conversion to the same type: `std::ops::Range` + --> $DIR/useless_conversion.rs:67:17 + | +LL | let mut n = NUMBERS.into_iter(); + | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` + error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:61:21 + --> $DIR/useless_conversion.rs:128:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:62:21 + --> $DIR/useless_conversion.rs:129:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:63:13 + --> $DIR/useless_conversion.rs:130:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:64:13 + --> $DIR/useless_conversion.rs:131:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:65:13 + --> $DIR/useless_conversion.rs:132:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> $DIR/useless_conversion.rs:66:13 + --> $DIR/useless_conversion.rs:133:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:67:21 + --> $DIR/useless_conversion.rs:134:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> $DIR/useless_conversion.rs:72:13 + --> $DIR/useless_conversion.rs:139:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:78:23 + --> $DIR/useless_conversion.rs:145:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:80:13 + --> $DIR/useless_conversion.rs:147:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter>` - --> $DIR/useless_conversion.rs:82:13 + --> $DIR/useless_conversion.rs:149:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` -error: aborting due to 14 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index ef55f1c31a88..0baec6f0b641 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -5,7 +5,6 @@ // the 2015 edition here is needed because edition 2018 changed the module system // (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint // no longer detects some of the cases starting with Rust 2018. -// FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] #![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index b81285142069..db591d56ab4d 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -5,7 +5,6 @@ // the 2015 edition here is needed because edition 2018 changed the module system // (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint // no longer detects some of the cases starting with Rust 2018. -// FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] #![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 626c1754fc82..6b469cdfc444 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:16:5 + --> $DIR/wildcard_imports.rs:15:5 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` @@ -7,85 +7,85 @@ LL | use crate::fn_mod::*; = note: `-D clippy::wildcard-imports` implied by `-D warnings` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:17:5 + --> $DIR/wildcard_imports.rs:16:5 | LL | use crate::mod_mod::*; | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:18:5 + --> $DIR/wildcard_imports.rs:17:5 | LL | use crate::multi_fn_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:20:5 + --> $DIR/wildcard_imports.rs:19:5 | LL | use crate::struct_mod::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:24:5 + --> $DIR/wildcard_imports.rs:23:5 | LL | use wildcard_imports_helper::inner::inner_for_self_import::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:25:5 + --> $DIR/wildcard_imports.rs:24:5 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:96:13 + --> $DIR/wildcard_imports.rs:95:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:102:75 + --> $DIR/wildcard_imports.rs:101:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:103:13 + --> $DIR/wildcard_imports.rs:102:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:20 + --> $DIR/wildcard_imports.rs:113:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:30 + --> $DIR/wildcard_imports.rs:113:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:121:13 + --> $DIR/wildcard_imports.rs:120:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:150:9 + --> $DIR/wildcard_imports.rs:149:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:159:9 + --> $DIR/wildcard_imports.rs:158:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:160:9 + --> $DIR/wildcard_imports.rs:159:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:171:13 + --> $DIR/wildcard_imports.rs:170:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:206:17 + --> $DIR/wildcard_imports.rs:205:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:214:13 + --> $DIR/wildcard_imports.rs:213:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:223:17 + --> $DIR/wildcard_imports.rs:222:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:232:13 + --> $DIR/wildcard_imports.rs:231:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:240:13 + --> $DIR/wildcard_imports.rs:239:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wildcard_imports_2021.edition2018.fixed b/tests/ui/wildcard_imports_2021.edition2018.fixed new file mode 100644 index 000000000000..6d534a10edcd --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -0,0 +1,240 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::foo; +use crate::mod_mod::inner_mod; +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::struct_mod::{A, inner_struct_mod}; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::{ExternA, extern_foo}; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::foo; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; + use wildcard_imports_helper::{ExternA, extern_foo}; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::inner_foo, inner2::inner_bar}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test::exported; + use crate:: fn_mod::foo; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.edition2018.stderr b/tests/ui/wildcard_imports_2021.edition2018.stderr new file mode 100644 index 000000000000..acca9f651b47 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:13:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:14:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:16:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:19:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:21:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:91:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:97:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:98:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:116:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:145:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:154:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:155:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:166:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:201:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:209:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:218:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:227:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:235:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + diff --git a/tests/ui/wildcard_imports_2021.edition2021.fixed b/tests/ui/wildcard_imports_2021.edition2021.fixed new file mode 100644 index 000000000000..6d534a10edcd --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -0,0 +1,240 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::foo; +use crate::mod_mod::inner_mod; +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::struct_mod::{A, inner_struct_mod}; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::{ExternA, extern_foo}; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::foo; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; + use wildcard_imports_helper::{ExternA, extern_foo}; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::inner_foo, inner2::inner_bar}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test::exported; + use crate:: fn_mod::foo; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.edition2021.stderr b/tests/ui/wildcard_imports_2021.edition2021.stderr new file mode 100644 index 000000000000..acca9f651b47 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:13:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:14:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:16:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:19:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:21:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:91:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:97:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:98:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:116:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:145:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:154:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:155:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:166:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:201:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:209:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:218:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:227:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:235:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + diff --git a/tests/ui/wildcard_imports_2021.rs b/tests/ui/wildcard_imports_2021.rs new file mode 100644 index 000000000000..b5ed58e68136 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.rs @@ -0,0 +1,241 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::*; +use crate::mod_mod::*; +use crate::multi_fn_mod::*; +use crate::struct_mod::*; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::*; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::*; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::*; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + use wildcard_imports_helper::*; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::*, inner2::*}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::*; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::*; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test:: * ; + use crate:: fn_mod:: + *; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::*; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::*; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::*; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::*; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.stderr b/tests/ui/wildcard_imports_2021.stderr new file mode 100644 index 000000000000..92f6d31530fa --- /dev/null +++ b/tests/ui/wildcard_imports_2021.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:9:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:10:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:11:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:12:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:17:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:87:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:93:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:94:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:105:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:105:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:112:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:141:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:150:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:151:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:162:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:197:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:205:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:214:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:223:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:231:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + From 315bb10405ec6082f46c73791bed24a134a307c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 27 Dec 2022 11:03:59 -0800 Subject: [PATCH 397/524] Account for multiple multiline spans with empty padding Instead of ``` LL | fn oom( | __^ | | _| | || LL | || ) { | ||_- LL | | } | |__^ ``` emit ``` LL | // fn oom( LL | || ) { | ||_- LL | | } | |__^ ``` --- tests/ui/async_yields_async.stderr | 6 ++---- tests/ui/result_map_unit_fn_unfixable.stderr | 5 +---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index 92ba35929678..22ce1c6f6471 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -3,8 +3,7 @@ error: an async construct yields a type which is itself awaitable | LL | let _h = async { | _____________________- -LL | | async { - | | _________^ +LL | |/ async { LL | || 3 LL | || } | ||_________^ awaitable value not awaited @@ -37,8 +36,7 @@ error: an async construct yields a type which is itself awaitable | LL | let _j = async || { | ________________________- -LL | | async { - | | _________^ +LL | |/ async { LL | || 3 LL | || } | ||_________^ awaitable value not awaited diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index 2e1eb8eb1806..d0e534f63568 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -19,10 +19,7 @@ LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:29:5 | -LL | x.field.map(|value| { - | ______^ - | | _____| - | || +LL | // x.field.map(|value| { LL | || do_nothing(value); LL | || do_nothing(value) LL | || }); From 0298095ac21e8fd5eb5ead8edc1b49a365972cc4 Mon Sep 17 00:00:00 2001 From: Ray Redondo Date: Thu, 29 Dec 2022 14:48:36 -0600 Subject: [PATCH 398/524] add comment about mutex_atomic issue to description --- clippy_lints/src/mutex_atomic.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 45bb217979aa..dc866ab6373b 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,6 +1,6 @@ //! Checks for uses of mutex where an atomic value could be used //! -//! This lint is **warn** by default +//! This lint is **allow** by default use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; @@ -20,6 +20,10 @@ declare_clippy_lint! { /// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and /// faster. /// + /// On the other hand, `Mutex`es are, in general, easier to + /// verify correctness. An atomic does not behave the same as + /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s commentary for more details. + /// /// ### Known problems /// This lint cannot detect if the mutex is actually used /// for waiting before a critical section. @@ -40,7 +44,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub MUTEX_ATOMIC, restriction, - "using a mutex where an atomic value could be used instead" + "using a mutex where an atomic value could be used instead." } declare_clippy_lint! { From ca5f4a1deea72a7a0c0654055195c6e98af9ef41 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 24 Dec 2022 23:26:25 -0500 Subject: [PATCH 399/524] option_if_let_else: update known problems wording --- clippy_lints/src/option_if_let_else.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 472f52380bbf..c5ea09590d3d 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -25,11 +25,11 @@ declare_clippy_lint! { /// Using the dedicated functions of the `Option` type is clearer and /// more concise than an `if let` expression. /// - /// ### Known problems - /// This lint uses a deliberately conservative metric for checking - /// if the inside of either body contains breaks or continues which will - /// cause it to not suggest a fix if either block contains a loop with - /// continues or breaks contained within the loop. + /// ### Notes + /// This lint uses a deliberately conservative metric for checking if the + /// inside of either body contains loop control expressions `break` or + /// `continue` (which cannot be used within closures). If these are found, + /// this lint will not be raised. /// /// ### Example /// ```rust From 7a64a51818d73b36ca4fd2ff2bbcdabe3dcd9a5c Mon Sep 17 00:00:00 2001 From: Ardis Lu Date: Sat, 31 Dec 2022 02:14:20 -0800 Subject: [PATCH 400/524] Fix typo --- book/src/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/installation.md b/book/src/installation.md index b2a28d0be622..cce888b17d4d 100644 --- a/book/src/installation.md +++ b/book/src/installation.md @@ -1,6 +1,6 @@ # Installation -If you're using `rustup` to install and manage you're Rust toolchains, Clippy is +If you're using `rustup` to install and manage your Rust toolchains, Clippy is usually **already installed**. In that case you can skip this chapter and go to the [Usage] chapter. From da7c99b81e7f863539a88f0d6ad2b79076d425f0 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 1 Jan 2023 06:21:28 -0500 Subject: [PATCH 401/524] Fix typo in `unused_self` diagnostic message --- clippy_lints/src/unused_self.rs | 2 +- tests/ui/unused_self.stderr | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 42bccc7212b3..18231b6a7e86 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { self_param.span, "unused `self` argument", None, - "consider refactoring to a associated function", + "consider refactoring to an associated function", ); } } diff --git a/tests/ui/unused_self.stderr b/tests/ui/unused_self.stderr index 23186122a9af..919f9b6efdab 100644 --- a/tests/ui/unused_self.stderr +++ b/tests/ui/unused_self.stderr @@ -4,7 +4,7 @@ error: unused `self` argument LL | fn unused_self_move(self) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function = note: `-D clippy::unused-self` implied by `-D warnings` error: unused `self` argument @@ -13,7 +13,7 @@ error: unused `self` argument LL | fn unused_self_ref(&self) {} | ^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:13:32 @@ -21,7 +21,7 @@ error: unused `self` argument LL | fn unused_self_mut_ref(&mut self) {} | ^^^^^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:14:32 @@ -29,7 +29,7 @@ error: unused `self` argument LL | fn unused_self_pin_ref(self: Pin<&Self>) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:15:36 @@ -37,7 +37,7 @@ error: unused `self` argument LL | fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:16:35 @@ -45,7 +45,7 @@ error: unused `self` argument LL | fn unused_self_pin_nested(self: Pin>) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:17:28 @@ -53,7 +53,7 @@ error: unused `self` argument LL | fn unused_self_box(self: Box) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:18:40 @@ -61,7 +61,7 @@ error: unused `self` argument LL | fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 { | ^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:21:37 @@ -69,7 +69,7 @@ error: unused `self` argument LL | fn unused_self_class_method(&self) { | ^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: aborting due to 9 previous errors From 5b46f2db594fc3f2e8c6d8c7f9274ba77736b67c Mon Sep 17 00:00:00 2001 From: chansuke Date: Thu, 22 Dec 2022 22:59:06 +0900 Subject: [PATCH 402/524] chore: fix identation of `if_chain` in `filter_map` --- clippy_lints/src/methods/filter_map.rs | 174 ++++++++++++------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index f888c58a72de..fc80f2eeae01 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -30,12 +30,12 @@ fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> match closure_expr.kind { hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => { if_chain! { - if ident.name == method_name; - if let hir::ExprKind::Path(path) = &receiver.kind; - if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id); - then { - return arg_id == *local - } + if ident.name == method_name; + if let hir::ExprKind::Path(path) = &receiver.kind; + if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id); + then { + return arg_id == *local + } } false }, @@ -92,92 +92,92 @@ pub(super) fn check( } if_chain! { - if is_trait_method(cx, map_recv, sym::Iterator); - - // filter(|x| ...is_some())... - if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind; - let filter_body = cx.tcx.hir().body(filter_body_id); - if let [filter_param] = filter_body.params; - // optional ref pattern: `filter(|&x| ..)` - let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { - (ref_pat, true) - } else { - (filter_param.pat, false) + if is_trait_method(cx, map_recv, sym::Iterator); + + // filter(|x| ...is_some())... + if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind; + let filter_body = cx.tcx.hir().body(filter_body_id); + if let [filter_param] = filter_body.params; + // optional ref pattern: `filter(|&x| ..)` + let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + (ref_pat, true) + } else { + (filter_param.pat, false) + }; + // closure ends with is_some() or is_ok() + if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; + if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind; + if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def(); + if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) { + Some(false) + } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) { + Some(true) + } else { + None + }; + if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; + + // ...map(|x| ...unwrap()) + if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind; + let map_body = cx.tcx.hir().body(map_body_id); + if let [map_param] = map_body.params; + if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; + // closure ends with expect() or unwrap() + if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); + + // .filter(..).map(|y| f(y).copied().unwrap()) + // ~~~~ + let map_arg_peeled = match map_arg.kind { + ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => { + original_arg + }, + _ => map_arg, + }; + + // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap()) + let simple_equal = path_to_local_id(filter_arg, filter_param_id) + && path_to_local_id(map_arg_peeled, map_param_id); + + let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + // in `filter(|x| ..)`, replace `*x` with `x` + let a_path = if_chain! { + if !is_filter_param_ref; + if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; + then { expr_path } else { a } }; - // closure ends with is_some() or is_ok() - if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; - if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind; - if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def(); - if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) { - Some(false) - } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) { - Some(true) + // let the filter closure arg and the map closure arg be equal + path_to_local_id(a_path, filter_param_id) + && path_to_local_id(b, map_param_id) + && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) + }; + + if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled); + then { + let span = filter_span.with_hi(expr.span.hi()); + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) } else { - None - }; - if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; - - // ...map(|x| ...unwrap()) - if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind; - let map_body = cx.tcx.hir().body(map_body_id); - if let [map_param] = map_body.params; - if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; - // closure ends with expect() or unwrap() - if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind; - if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); - - // .filter(..).map(|y| f(y).copied().unwrap()) - // ~~~~ - let map_arg_peeled = match map_arg.kind { - ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => { - original_arg - }, - _ => map_arg, + ("filter", MANUAL_FILTER_MAP) }; + let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); + let (to_opt, deref) = if is_result { + (".ok()", String::new()) + } else { + let derefs = cx.typeck_results() + .expr_adjustments(map_arg) + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); - // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap()) - let simple_equal = path_to_local_id(filter_arg, filter_param_id) - && path_to_local_id(map_arg_peeled, map_param_id); - - let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - // in `filter(|x| ..)`, replace `*x` with `x` - let a_path = if_chain! { - if !is_filter_param_ref; - if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; - then { expr_path } else { a } - }; - // let the filter closure arg and the map closure arg be equal - path_to_local_id(a_path, filter_param_id) - && path_to_local_id(b, map_param_id) - && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) + ("", "*".repeat(derefs)) }; - - if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled); - then { - let span = filter_span.with_hi(expr.span.hi()); - let (filter_name, lint) = if is_find { - ("find", MANUAL_FIND_MAP) - } else { - ("filter", MANUAL_FILTER_MAP) - }; - let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); - let (to_opt, deref) = if is_result { - (".ok()", String::new()) - } else { - let derefs = cx.typeck_results() - .expr_adjustments(map_arg) - .iter() - .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) - .count(); - - ("", "*".repeat(derefs)) - }; - let sugg = format!( - "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", - snippet(cx, map_arg.span, ".."), - ); - span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); - } + let sugg = format!( + "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", + snippet(cx, map_arg.span, ".."), + ); + span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); + } } } From 6145194b687b67e65049ac8d9ec730ebadfc34d7 Mon Sep 17 00:00:00 2001 From: chansuke Date: Thu, 22 Dec 2022 23:30:51 +0900 Subject: [PATCH 403/524] chore: add simple comment for `get_enclosing_block` --- clippy_utils/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 43e2d1ec826c..c3eb4e229ca0 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1304,6 +1304,7 @@ pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) } } +/// Gets the enclosing block, if any. pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> { let map = &cx.tcx.hir(); let enclosing_node = map From 8de011fdf72849e6dc25555bc1800632a66615f6 Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Sun, 1 Jan 2023 21:23:32 -0500 Subject: [PATCH 404/524] don't lint field_reassign when field in closure This commit makes the ContainsName struct visit all interior expressions, which means that ContainsName will return true even if `name` is used in a closure within `expr`. --- clippy_lints/src/copies.rs | 6 +++++- clippy_lints/src/default.rs | 2 +- clippy_lints/src/loops/needless_range_loop.rs | 4 ++-- clippy_utils/src/lib.rs | 21 +++++++++++++++---- tests/ui/field_reassign_with_default.rs | 21 +++++++++++++++++++ 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 0e3d9317590f..f10c35cde52a 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -525,7 +525,11 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo .iter() .filter(|&&(_, name)| !name.as_str().starts_with('_')) .any(|&(_, name)| { - let mut walker = ContainsName { name, result: false }; + let mut walker = ContainsName { + name, + result: false, + cx, + }; // Scan block block diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 7f937de1dd31..55f6121a09d2 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -170,7 +170,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { // find out if and which field was set by this `consecutive_statement` if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) { // interrupt and cancel lint if assign_rhs references the original binding - if contains_name(binding_name, assign_rhs) { + if contains_name(binding_name, assign_rhs, cx) { cancel_lint = true; break; } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 27ba27202bf7..3bca93d80aa7 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -81,7 +81,7 @@ pub(super) fn check<'tcx>( let skip = if starts_at_zero { String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) { + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) { return; } else { format!(".skip({})", snippet(cx, start.span, "..")) @@ -109,7 +109,7 @@ pub(super) fn check<'tcx>( if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) { return; } else { match limits { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 43e2d1ec826c..b2175c5cb169 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -116,6 +116,8 @@ use crate::consts::{constant, Constant}; use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param}; use crate::visitors::for_each_expr; +use rustc_middle::hir::nested_filter; + #[macro_export] macro_rules! extract_msrv_attr { ($context:ident) => { @@ -1253,22 +1255,33 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } } -pub struct ContainsName { +pub struct ContainsName<'a, 'tcx> { + pub cx: &'a LateContext<'tcx>, pub name: Symbol, pub result: bool, } -impl<'tcx> Visitor<'tcx> for ContainsName { +impl<'a, 'tcx> Visitor<'tcx> for ContainsName<'a, 'tcx> { + type NestedFilter = nested_filter::All; + fn visit_name(&mut self, name: Symbol) { if self.name == name { self.result = true; } } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } } /// Checks if an `Expr` contains a certain name. -pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { - let mut cn = ContainsName { name, result: false }; +pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool { + let mut cn = ContainsName { + name, + result: false, + cx, + }; cn.visit_expr(expr); cn.result } diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 7367910eaa12..1f989bb12205 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -247,3 +247,24 @@ mod issue6312 { } } } + +struct Collection { + items: Vec, + len: usize, +} + +impl Default for Collection { + fn default() -> Self { + Self { + items: vec![1, 2, 3], + len: 0, + } + } +} + +#[allow(clippy::redundant_closure_call)] +fn issue10136() { + let mut c = Collection::default(); + // don't lint, since c.items was used to calculate this value + c.len = (|| c.items.len())(); +} From 01a2a9df4300749f5c96209e707d02aae44f39e3 Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Sun, 1 Jan 2023 19:10:21 -0500 Subject: [PATCH 405/524] [`drop_ref`]: don't lint idiomatic in match arm --- clippy_lints/src/drop_forget_ref.rs | 2 +- tests/ui/drop_ref.rs | 23 +++++++++++++++++ tests/ui/drop_ref.stderr | 38 ++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 4721a7b37056..11e1bcdf12d1 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { let is_copy = is_copy(cx, arg_ty); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let (lint, msg) = match fn_name { - sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY), + sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => (DROP_REF, DROP_REF_SUMMARY), sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY), sym::mem_drop if is_copy && !drop_is_single_call_in_arm => (DROP_COPY, DROP_COPY_SUMMARY), sym::mem_forget if is_copy => (FORGET_COPY, FORGET_COPY_SUMMARY), diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index 7de0b0bbdf9a..10044e65f115 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -72,3 +72,26 @@ fn test_owl_result_2() -> Result { produce_half_owl_ok().map(drop)?; Ok(1) } + +#[allow(unused)] +#[allow(clippy::unit_cmp)] +fn issue10122(x: u8) { + // This is a function which returns a reference and has a side-effect, which means + // that calling drop() on the function is considered an idiomatic way of achieving the side-effect + // in a match arm. + fn println_and(t: &T) -> &T { + println!("foo"); + t + } + + match x { + 0 => drop(println_and(&12)), // Don't lint (copy type), we only care about side-effects + 1 => drop(println_and(&String::new())), // Don't lint (no copy type), we only care about side-effects + 2 => { + drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block + }, + 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + 4 => drop(&2), // Lint, not a fn/method call + _ => (), + } +} diff --git a/tests/ui/drop_ref.stderr b/tests/ui/drop_ref.stderr index 4743cf79b5d3..293b9f6de832 100644 --- a/tests/ui/drop_ref.stderr +++ b/tests/ui/drop_ref.stderr @@ -107,5 +107,41 @@ note: argument has type `&SomeStruct` LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing + --> $DIR/drop_ref.rs:91:13 + | +LL | drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&i32` + --> $DIR/drop_ref.rs:91:18 + | +LL | drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing + --> $DIR/drop_ref.rs:93:14 + | +LL | 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&i32` + --> $DIR/drop_ref.rs:93:19 + | +LL | 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing + --> $DIR/drop_ref.rs:94:14 + | +LL | 4 => drop(&2), // Lint, not a fn/method call + | ^^^^^^^^ + | +note: argument has type `&i32` + --> $DIR/drop_ref.rs:94:19 + | +LL | 4 => drop(&2), // Lint, not a fn/method call + | ^^ + +error: aborting due to 12 previous errors From 7a1a9c8188eed7212eab35c820dcdff88cf0f3df Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Mon, 2 Jan 2023 14:58:10 +0100 Subject: [PATCH 406/524] empty_structs_with_brackets: not MachineApplicable anymore --- clippy_lints/src/empty_structs_with_brackets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index 08bf80a42290..c3a020433de8 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -45,7 +45,7 @@ impl EarlyLintPass for EmptyStructsWithBrackets { span_after_ident, "remove the brackets", ";", - Applicability::MachineApplicable); + Applicability::Unspecified); }, ); } From 9aef1a264a52d59e959e4b0a2f9a71d06c3def13 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Sun, 1 Jan 2023 02:41:45 -0500 Subject: [PATCH 407/524] reword dbg_macro labels --- clippy_lints/src/dbg_macro.rs | 10 ++--- tests/ui-toml/dbg_macro/dbg_macro.stderr | 36 ++++++++-------- tests/ui/dbg_macro.stderr | 52 ++++++++++++------------ 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index fe9f4f9ae3cb..799e71e847a9 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -10,11 +10,11 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// Checks for usage of dbg!() macro. + /// Checks for usage of the [`dbg!`](https://doc.rust-lang.org/std/macro.dbg.html) macro. /// /// ### Why is this bad? - /// `dbg!` macro is intended as a debugging tool. It - /// should not be in version control. + /// The `dbg!` macro is intended as a debugging tool. It should not be present in released + /// software or committed to a version control system. /// /// ### Example /// ```rust,ignore @@ -91,8 +91,8 @@ impl LateLintPass<'_> for DbgMacro { cx, DBG_MACRO, macro_call.span, - "`dbg!` macro is intended as a debugging tool", - "ensure to avoid having uses of it in version control", + "the `dbg!` macro is intended as a debugging tool", + "remove the invocation before committing it to a version control system", suggestion, applicability, ); diff --git a/tests/ui-toml/dbg_macro/dbg_macro.stderr b/tests/ui-toml/dbg_macro/dbg_macro.stderr index 46efb86dcfc5..859383a71194 100644 --- a/tests/ui-toml/dbg_macro/dbg_macro.stderr +++ b/tests/ui-toml/dbg_macro/dbg_macro.stderr @@ -1,99 +1,99 @@ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:5:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:9:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if n <= 1 { | ~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:10:9 | LL | dbg!(1) | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1 | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:12:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | n * factorial(n - 1) | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:17:5 | LL | dbg!(42); | ^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 42; | ~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:18:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | dbg!(dbg!(42)); | ~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:19:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:20:5 | LL | dbg!(1, 2, dbg!(3, 4)); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, dbg!(3, 4)); | ~~~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:21:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ diff --git a/tests/ui/dbg_macro.stderr b/tests/ui/dbg_macro.stderr index e6a65b46d975..ddb5f1342e99 100644 --- a/tests/ui/dbg_macro.stderr +++ b/tests/ui/dbg_macro.stderr @@ -1,143 +1,143 @@ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:5:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:9:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if n <= 1 { | ~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:10:9 | LL | dbg!(1) | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1 | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:12:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | n * factorial(n - 1) | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:17:5 | LL | dbg!(42); | ^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 42; | ~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:18:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | dbg!(dbg!(42)); | ~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:19:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:20:5 | LL | dbg!(1, 2, dbg!(3, 4)); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, dbg!(3, 4)); | ~~~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:21:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:41:9 | LL | dbg!(2); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 2; | ~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:47:5 | LL | dbg!(1); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1; | ~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:52:5 | LL | dbg!(1); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1; | ~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:58:9 | LL | dbg!(1); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1; | ~ From f53c6e2f15a54dee84cbf20228a54861fa66bf09 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 2 Jan 2023 19:58:00 +0100 Subject: [PATCH 408/524] Correct Gankra's name in the linkedlist lint --- clippy_lints/src/types/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 20978e81dc58..b0d49d268569 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -127,7 +127,7 @@ declare_clippy_lint! { /// `Vec` or a `VecDeque` (formerly called `RingBuf`). /// /// ### Why is this bad? - /// Gankro says: + /// Gankra says: /// /// > The TL;DR of `LinkedList` is that it's built on a massive amount of /// pointers and indirection. From bd83650e91a751ad352444b458f9f50549fac208 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Wed, 21 Dec 2022 12:47:39 -0700 Subject: [PATCH 409/524] Suggest using Path for comparing extensions Signed-off-by: Tyler Weaver --- ...se_sensitive_file_extension_comparisons.rs | 38 +++++++-- ...sensitive_file_extension_comparisons.fixed | 64 +++++++++++++++ ...se_sensitive_file_extension_comparisons.rs | 8 +- ...ensitive_file_extension_comparisons.stderr | 82 ++++++++++++++++--- 4 files changed, 175 insertions(+), 17 deletions(-) create mode 100644 tests/ui/case_sensitive_file_extension_comparisons.fixed diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index d226c0bba659..bc9b7a73b5b1 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,7 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_span::{source_map::Spanned, Span}; @@ -28,13 +30,39 @@ pub(super) fn check<'tcx>( let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs(); if recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String); then { - span_lint_and_help( + span_lint_and_then( cx, CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - call_span, + recv.span.to(call_span), "case-sensitive file extension comparison", - None, - "consider using a case-insensitive comparison instead", + |diag| { + diag.help("consider using a case-insensitive comparison instead"); + let mut recv_str = Sugg::hir(cx, recv, "").to_string(); + + if is_type_lang_item(cx, recv_ty, LangItem::String) { + recv_str = format!("&{recv_str}"); + } + + if recv_str.contains(".to_lowercase()") { + diag.note("to_lowercase allocates memory, this can be avoided by using Path"); + recv_str = recv_str.replace(".to_lowercase()", ""); + } + + if recv_str.contains(".to_uppercase()") { + diag.note("to_uppercase allocates memory, this can be avoided by using Path"); + recv_str = recv_str.replace(".to_uppercase()", ""); + } + + diag.span_suggestion( + recv.span.to(call_span), + "use std::path::Path", + format!("std::path::Path::new({}) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", + recv_str, ext_str.strip_prefix('.').unwrap()), + Applicability::MaybeIncorrect, + ); + } ); } } diff --git a/tests/ui/case_sensitive_file_extension_comparisons.fixed b/tests/ui/case_sensitive_file_extension_comparisons.fixed new file mode 100644 index 000000000000..a8b5950f2087 --- /dev/null +++ b/tests/ui/case_sensitive_file_extension_comparisons.fixed @@ -0,0 +1,64 @@ +// run-rustfix +#![warn(clippy::case_sensitive_file_extension_comparisons)] + +use std::string::String; + +struct TestStruct; + +impl TestStruct { + fn ends_with(self, _arg: &str) {} +} + +#[allow(dead_code)] +fn is_rust_file(filename: &str) -> bool { + std::path::Path::new(filename) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) +} + +fn main() { + // std::string::String and &str should trigger the lint failure with .ext12 + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + let _ = std::path::Path::new("str") + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + + // The test struct should not trigger the lint failure with .ext12 + TestStruct {}.ends_with(".ext12"); + + // std::string::String and &str should trigger the lint failure with .EXT12 + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + let _ = std::path::Path::new("str") + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + + // This should print a note about how to_lowercase and to_uppercase allocates + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + + // The test struct should not trigger the lint failure with .EXT12 + TestStruct {}.ends_with(".EXT12"); + + // Should not trigger the lint failure with .eXT12 + let _ = String::new().ends_with(".eXT12"); + let _ = "str".ends_with(".eXT12"); + TestStruct {}.ends_with(".eXT12"); + + // Should not trigger the lint failure with .EXT123 (too long) + let _ = String::new().ends_with(".EXT123"); + let _ = "str".ends_with(".EXT123"); + TestStruct {}.ends_with(".EXT123"); + + // Shouldn't fail if it doesn't start with a dot + let _ = String::new().ends_with("a.ext"); + let _ = "str".ends_with("a.extA"); + TestStruct {}.ends_with("a.ext"); +} diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs index 6f0485b5279b..ee9bcd73d52e 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::case_sensitive_file_extension_comparisons)] use std::string::String; @@ -5,9 +6,10 @@ use std::string::String; struct TestStruct; impl TestStruct { - fn ends_with(self, arg: &str) {} + fn ends_with(self, _arg: &str) {} } +#[allow(dead_code)] fn is_rust_file(filename: &str) -> bool { filename.ends_with(".rs") } @@ -24,6 +26,10 @@ fn main() { let _ = String::new().ends_with(".EXT12"); let _ = "str".ends_with(".EXT12"); + // This should print a note about how to_lowercase and to_uppercase allocates + let _ = String::new().to_lowercase().ends_with(".EXT12"); + let _ = String::new().to_uppercase().ends_with(".EXT12"); + // The test struct should not trigger the lint failure with .EXT12 TestStruct {}.ends_with(".EXT12"); diff --git a/tests/ui/case_sensitive_file_extension_comparisons.stderr b/tests/ui/case_sensitive_file_extension_comparisons.stderr index a28dd8bd5ad3..33435f086d43 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.stderr +++ b/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -1,43 +1,103 @@ error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:12:14 + --> $DIR/case_sensitive_file_extension_comparisons.rs:14:5 | LL | filename.ends_with(".rs") - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead = note: `-D clippy::case-sensitive-file-extension-comparisons` implied by `-D warnings` +help: use std::path::Path + | +LL ~ std::path::Path::new(filename) +LL + .extension() +LL + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:17:27 + --> $DIR/case_sensitive_file_extension_comparisons.rs:19:13 | LL | let _ = String::new().ends_with(".ext12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:18:19 + --> $DIR/case_sensitive_file_extension_comparisons.rs:20:13 | LL | let _ = "str".ends_with(".ext12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new("str") +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:24:27 + --> $DIR/case_sensitive_file_extension_comparisons.rs:26:13 | LL | let _ = String::new().ends_with(".EXT12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:25:19 + --> $DIR/case_sensitive_file_extension_comparisons.rs:27:13 | LL | let _ = "str".ends_with(".EXT12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new("str") +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + | + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:30:13 + | +LL | let _ = String::new().to_lowercase().ends_with(".EXT12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead + = note: to_lowercase allocates memory, this can be avoided by using Path +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + | + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:31:13 + | +LL | let _ = String::new().to_uppercase().ends_with(".EXT12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead + = note: to_uppercase allocates memory, this can be avoided by using Path +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + | -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors From 0aa7d73df37470b9f868873587faf64c3339f964 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Mon, 2 Jan 2023 07:12:43 -0700 Subject: [PATCH 410/524] Only remove method from end of recv, indent suggestion source Signed-off-by: Tyler Weaver --- ...se_sensitive_file_extension_comparisons.rs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index bc9b7a73b5b1..e34c377f5fc3 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; @@ -37,29 +38,36 @@ pub(super) fn check<'tcx>( "case-sensitive file extension comparison", |diag| { diag.help("consider using a case-insensitive comparison instead"); - let mut recv_str = Sugg::hir(cx, recv, "").to_string(); + let mut recv_source = Sugg::hir(cx, recv, "").to_string(); if is_type_lang_item(cx, recv_ty, LangItem::String) { - recv_str = format!("&{recv_str}"); + recv_source = format!("&{recv_source}"); } - if recv_str.contains(".to_lowercase()") { + if recv_source.ends_with(".to_lowercase()") { diag.note("to_lowercase allocates memory, this can be avoided by using Path"); - recv_str = recv_str.replace(".to_lowercase()", ""); + recv_source = recv_source.strip_suffix(".to_lowercase()").unwrap().to_string(); } - if recv_str.contains(".to_uppercase()") { + if recv_source.ends_with(".to_uppercase()") { diag.note("to_uppercase allocates memory, this can be avoided by using Path"); - recv_str = recv_str.replace(".to_uppercase()", ""); + recv_source = recv_source.strip_suffix(".to_uppercase()").unwrap().to_string(); } + let suggestion_source = reindent_multiline( + format!( + "std::path::Path::new({}) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", + recv_source, ext_str.strip_prefix('.').unwrap()).into(), + true, + Some(indent_of(cx, call_span).unwrap_or(0) + 4) + ); + diag.span_suggestion( recv.span.to(call_span), "use std::path::Path", - format!("std::path::Path::new({}) - .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", - recv_str, ext_str.strip_prefix('.').unwrap()), + suggestion_source, Applicability::MaybeIncorrect, ); } From cf4e9813b0fee5a8a0d7e93eadd4d51d0492a9d6 Mon Sep 17 00:00:00 2001 From: Eric Wu Date: Mon, 2 Jan 2023 18:35:21 -0500 Subject: [PATCH 411/524] use OnlyBodies instead of All we only need to check closures, so nestedfilter::All was overkill here. --- clippy_utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b2175c5cb169..46769cf2392b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1262,7 +1262,7 @@ pub struct ContainsName<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for ContainsName<'a, 'tcx> { - type NestedFilter = nested_filter::All; + type NestedFilter = nested_filter::OnlyBodies; fn visit_name(&mut self, name: Symbol) { if self.name == name { From 0cee2c50952a11099692652af367b4fea3cb09a6 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Mon, 2 Jan 2023 16:42:56 -0700 Subject: [PATCH 412/524] Don't trigger lint if last method is to_lower/upper --- ...se_sensitive_file_extension_comparisons.rs | 17 ++++------ ...sensitive_file_extension_comparisons.fixed | 10 ++---- ...se_sensitive_file_extension_comparisons.rs | 2 +- ...ensitive_file_extension_comparisons.stderr | 32 +------------------ 4 files changed, 12 insertions(+), 49 deletions(-) diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index e34c377f5fc3..b6ec0fba6cfb 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -18,6 +18,13 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, ) { + if let ExprKind::MethodCall(path_segment, ..) = recv.kind { + let method_name = path_segment.ident.name.as_str(); + if method_name == "to_lowercase" || method_name == "to_uppercase" { + return; + } + } + if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); @@ -44,16 +51,6 @@ pub(super) fn check<'tcx>( recv_source = format!("&{recv_source}"); } - if recv_source.ends_with(".to_lowercase()") { - diag.note("to_lowercase allocates memory, this can be avoided by using Path"); - recv_source = recv_source.strip_suffix(".to_lowercase()").unwrap().to_string(); - } - - if recv_source.ends_with(".to_uppercase()") { - diag.note("to_uppercase allocates memory, this can be avoided by using Path"); - recv_source = recv_source.strip_suffix(".to_uppercase()").unwrap().to_string(); - } - let suggestion_source = reindent_multiline( format!( "std::path::Path::new({}) diff --git a/tests/ui/case_sensitive_file_extension_comparisons.fixed b/tests/ui/case_sensitive_file_extension_comparisons.fixed index a8b5950f2087..a19ed1ddcd54 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.fixed +++ b/tests/ui/case_sensitive_file_extension_comparisons.fixed @@ -36,13 +36,9 @@ fn main() { .extension() .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); - // This should print a note about how to_lowercase and to_uppercase allocates - let _ = std::path::Path::new(&String::new()) - .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); - let _ = std::path::Path::new(&String::new()) - .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase + let _ = String::new().to_lowercase().ends_with(".EXT12"); + let _ = String::new().to_uppercase().ends_with(".EXT12"); // The test struct should not trigger the lint failure with .EXT12 TestStruct {}.ends_with(".EXT12"); diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs index ee9bcd73d52e..ad56b7296f75 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -26,7 +26,7 @@ fn main() { let _ = String::new().ends_with(".EXT12"); let _ = "str".ends_with(".EXT12"); - // This should print a note about how to_lowercase and to_uppercase allocates + // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase let _ = String::new().to_lowercase().ends_with(".EXT12"); let _ = String::new().to_uppercase().ends_with(".EXT12"); diff --git a/tests/ui/case_sensitive_file_extension_comparisons.stderr b/tests/ui/case_sensitive_file_extension_comparisons.stderr index 33435f086d43..b5c8e4b4fe68 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.stderr +++ b/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -69,35 +69,5 @@ LL + .extension() LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); | -error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:30:13 - | -LL | let _ = String::new().to_lowercase().ends_with(".EXT12"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using a case-insensitive comparison instead - = note: to_lowercase allocates memory, this can be avoided by using Path -help: use std::path::Path - | -LL ~ let _ = std::path::Path::new(&String::new()) -LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); - | - -error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:31:13 - | -LL | let _ = String::new().to_uppercase().ends_with(".EXT12"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using a case-insensitive comparison instead - = note: to_uppercase allocates memory, this can be avoided by using Path -help: use std::path::Path - | -LL ~ let _ = std::path::Path::new(&String::new()) -LL + .extension() -LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); - | - -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors From d3a50d2fda4fd1a024dfe7b01ab69fb5a0a6b7a7 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Wed, 4 Jan 2023 00:44:20 +0100 Subject: [PATCH 413/524] trim paths in `box_default` --- clippy_lints/src/box_default.rs | 4 ++-- tests/ui/box_default.fixed | 8 ++++---- tests/ui/box_default.stderr | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 91900542af83..9d98a6bab710 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -8,7 +8,7 @@ use rustc_hir::{ Block, Expr, ExprKind, Local, Node, QPath, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; +use rustc_middle::{lint::in_external_macro, ty::print::with_forced_trimmed_paths}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -59,7 +59,7 @@ impl LateLintPass<'_> for BoxDefault { if is_plain_default(arg_path) || given_type(cx, expr) { "Box::default()".into() } else { - format!("Box::<{arg_ty}>::default()") + with_forced_trimmed_paths!(format!("Box::<{arg_ty}>::default()")) }, Applicability::MachineApplicable ); diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed index 911fa856aa0a..68ab996a7049 100644 --- a/tests/ui/box_default.fixed +++ b/tests/ui/box_default.fixed @@ -21,16 +21,16 @@ macro_rules! outer { fn main() { let _string: Box = Box::default(); let _byte = Box::::default(); - let _vec = Box::>::default(); + let _vec = Box::>::default(); let _impl = Box::::default(); let _impl2 = Box::::default(); let _impl3: Box = Box::default(); let _own = Box::new(OwnDefault::default()); // should not lint - let _in_macro = outer!(Box::::default()); - let _string_default = outer!(Box::::default()); + let _in_macro = outer!(Box::::default()); + let _string_default = outer!(Box::::default()); let _vec2: Box> = Box::default(); let _vec3: Box> = Box::default(); - let _vec4: Box<_> = Box::>::default(); + let _vec4: Box<_> = Box::>::default(); let _more = ret_ty_fn(); call_ty_fn(Box::default()); } diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index 5ea410331afb..f77c97cdfa26 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -16,7 +16,7 @@ error: `Box::new(_)` of default value --> $DIR/box_default.rs:24:16 | LL | let _vec = Box::new(Vec::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:25:17 @@ -40,13 +40,13 @@ error: `Box::new(_)` of default value --> $DIR/box_default.rs:29:28 | LL | let _in_macro = outer!(Box::new(String::new())); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:30:34 | LL | let _string_default = outer!(Box::new(String::from(""))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:31:46 @@ -64,7 +64,7 @@ error: `Box::new(_)` of default value --> $DIR/box_default.rs:33:25 | LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0])); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:35:16 From 73d293fb6df79060f483f3eb1b3f59af7b6493a9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 3 Jan 2023 07:31:04 +0000 Subject: [PATCH 414/524] rename get_parent_node to parent_id --- clippy_lints/src/escape.rs | 6 +++--- clippy_lints/src/index_refutable_slice.rs | 4 ++-- clippy_lints/src/loops/same_item_push.rs | 2 +- clippy_lints/src/manual_rem_euclid.rs | 2 +- clippy_lints/src/matches/match_single_binding.rs | 6 +++--- clippy_lints/src/mixed_read_write_in_expression.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/non_copy_const.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/unit_types/unit_arg.rs | 4 ++-- clippy_lints/src/unnecessary_wraps.rs | 2 +- clippy_lints/src/utils/internal_lints/metadata_collector.rs | 2 +- .../src/utils/internal_lints/unnecessary_def_path.rs | 2 +- clippy_utils/src/lib.rs | 4 ++-- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 1d09adec12f3..58b7b9829a10 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -131,7 +131,7 @@ fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool { _ => return false, } - matches!(map.find(map.get_parent_node(id)), Some(Node::Param(_))) + matches!(map.find(map.parent_id(id)), Some(Node::Param(_))) } impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { @@ -156,8 +156,8 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { let map = &self.cx.tcx.hir(); if is_argument(*map, cmt.hir_id) { // Skip closure arguments - let parent_id = map.get_parent_node(cmt.hir_id); - if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) { + let parent_id = map.parent_id(cmt.hir_id); + if let Some(Node::Expr(..)) = map.find(map.parent_id(parent_id)) { return; } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index cf35b1f175c6..bdeddf44df7b 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -251,7 +251,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { let map = cx.tcx.hir(); // Checking for slice indexing - let parent_id = map.get_parent_node(expr.hir_id); + let parent_id = map.parent_id(expr.hir_id); if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id); if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind; if let Some((Constant::Int(index_value), _)) = constant(cx, cx.typeck_results(), index_expr); @@ -259,7 +259,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { if index_value < max_suggested_slice; // Make sure that this slice index is read only - let maybe_addrof_id = map.get_parent_node(parent_id); + let maybe_addrof_id = map.parent_id(parent_id); if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id); if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind; then { diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 07edee46fa65..540656a2cd99 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -63,7 +63,7 @@ pub(super) fn check<'tcx>( if let Node::Pat(pat) = node; if let PatKind::Binding(bind_ann, ..) = pat.kind; if !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)); - let parent_node = cx.tcx.hir().get_parent_node(hir_id); + let parent_node = cx.tcx.hir().parent_id(hir_id); if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); if let Some(init) = parent_let_expr.init; then { diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 8d447c37150b..494fde395e93 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && let Some(hir_id) = path_to_local(expr3) && let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id) { // Apply only to params or locals with annotated types - match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + match cx.tcx.hir().find(cx.tcx.hir().parent_id(hir_id)) { Some(Node::Param(..)) => (), Some(Node::Local(local)) => { let Some(ty) = local.ty else { return }; diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index c94a1f763306..abe9d231f4aa 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -140,8 +140,8 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option { let map = &cx.tcx.hir(); - if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)) { - return match map.find(map.get_parent_node(parent_arm_expr.hir_id)) { + if let Some(Node::Expr(parent_arm_expr)) = map.find(map.parent_id(ex.hir_id)) { + return match map.find(map.parent_id(parent_arm_expr.hir_id)) { Some(Node::Local(parent_let_expr)) => Some(AssignmentExpr::Local { span: parent_let_expr.span, pat_span: parent_let_expr.pat.span(), @@ -183,7 +183,7 @@ fn sugg_with_curlies<'a>( // If the parent is already an arm, and the body is another match statement, // we need curly braces around suggestion - let parent_node_id = cx.tcx.hir().get_parent_node(match_expr.hir_id); + let parent_node_id = cx.tcx.hir().parent_id(match_expr.hir_id); if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { if let ExprKind::Match(..) = arm.body.kind { cbrace_end = format!("\n{indent}}}"); diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 321fa4b7f999..f0be7771bb1a 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -186,7 +186,7 @@ fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) { let map = &vis.cx.tcx.hir(); let mut cur_id = vis.write_expr.hir_id; loop { - let parent_id = map.get_parent_node(cur_id); + let parent_id = map.parent_id(cur_id); if parent_id == cur_id { break; } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 2f0b7ce16e51..58c54280a234 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().parent_id(hir_id)) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 2a3bd4ee6ce6..07fd321d69fc 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -366,7 +366,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { let mut dereferenced_expr = expr; let mut needs_check_adjustment = true; loop { - let parent_id = cx.tcx.hir().get_parent_node(cur_expr.hir_id); + let parent_id = cx.tcx.hir().parent_id(cur_expr.hir_id); if parent_id == cur_expr.hir_id { break; } diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 75add4ee4aad..f96a19b27235 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -299,7 +299,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().parent_id(hir_id)) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index ef9f740f7047..ac4f8789a434 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { return; } let map = &cx.tcx.hir(); - let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); + let opt_parent_node = map.find(map.parent_id(expr.hir_id)); if_chain! { if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; if is_questionmark_desugar_marked_call(parent_expr); @@ -192,7 +192,7 @@ fn fmt_stmts_and_call( let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); // expr is not in a block statement or result expression position, wrap in a block - let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id)); + let parent_node = cx.tcx.hir().find(cx.tcx.hir().parent_id(call_expr.hir_id)); if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { let block_indent = call_expr_indent + 4; stmts_and_call_snippet = diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 60b46854b4ff..63f4f01b0877 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } // Abort if the method is implementing a trait or of it a trait method. - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().parent_id(hir_id)) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 929544cd69d5..a177ae507bbe 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -1058,7 +1058,7 @@ fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) - fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { let map = cx.tcx.hir(); - match map.find(map.get_parent_node(hir_id)) { + match map.find(map.parent_id(hir_id)) { Some(hir::Node::Local(local)) => Some(local), Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id), _ => None, diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 393988dbad38..7144363637a0 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -219,7 +219,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { - let parent_id = cx.tcx.hir().get_parent_node(hir_id); + let parent_id = cx.tcx.hir().parent_id(hir_id); if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { path_to_matched_type(cx, init) } else { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d863609b6a72..63d0938169a8 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -174,7 +174,7 @@ pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option< if_chain! { if let Some(Node::Pat(pat)) = hir.find(hir_id); if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)); - let parent = hir.get_parent_node(hir_id); + let parent = hir.parent_id(hir_id); if let Some(Node::Local(local)) = hir.find(parent); then { return local.init; @@ -2075,7 +2075,7 @@ pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { /// } /// ``` pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().parent_id(hir_id)) { matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) } else { false From bd1d8971cf3fe6f3cefcf7cc03dbdc7d1c684617 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 3 Jan 2023 07:31:33 +0000 Subject: [PATCH 415/524] rename find_parent_node to opt_parent_id --- clippy_lints/src/casts/cast_slice_different_sizes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index c8e54d7b8e0c..27cc5a1c3f04 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -68,7 +68,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let map = cx.tcx.hir(); if_chain! { - if let Some(parent_id) = map.find_parent_node(expr.hir_id); + if let Some(parent_id) = map.opt_parent_id(expr.hir_id); if let Some(parent) = map.find(parent_id); then { let expr = match parent { From 70f6c478f65993f4150f5f41b33f57d7b2715f3b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 3 Jan 2023 17:30:35 +0000 Subject: [PATCH 416/524] get_parent and find_parent --- clippy_lints/src/escape.rs | 4 ++-- clippy_lints/src/manual_rem_euclid.rs | 2 +- clippy_lints/src/matches/match_single_binding.rs | 7 +++---- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/unit_types/unit_arg.rs | 4 ++-- clippy_lints/src/unnecessary_wraps.rs | 2 +- .../src/utils/internal_lints/metadata_collector.rs | 2 +- clippy_utils/src/lib.rs | 4 ++-- 9 files changed, 14 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 58b7b9829a10..dfb43893326e 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -131,7 +131,7 @@ fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool { _ => return false, } - matches!(map.find(map.parent_id(id)), Some(Node::Param(_))) + matches!(map.find_parent(id), Some(Node::Param(_))) } impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { @@ -157,7 +157,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { if is_argument(*map, cmt.hir_id) { // Skip closure arguments let parent_id = map.parent_id(cmt.hir_id); - if let Some(Node::Expr(..)) = map.find(map.parent_id(parent_id)) { + if let Some(Node::Expr(..)) = map.find_parent(parent_id) { return; } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 494fde395e93..38f41d077c16 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && let Some(hir_id) = path_to_local(expr3) && let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id) { // Apply only to params or locals with annotated types - match cx.tcx.hir().find(cx.tcx.hir().parent_id(hir_id)) { + match cx.tcx.hir().find_parent(hir_id) { Some(Node::Param(..)) => (), Some(Node::Local(local)) => { let Some(ty) = local.ty else { return }; diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index abe9d231f4aa..065a5c72621c 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -140,8 +140,8 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option { let map = &cx.tcx.hir(); - if let Some(Node::Expr(parent_arm_expr)) = map.find(map.parent_id(ex.hir_id)) { - return match map.find(map.parent_id(parent_arm_expr.hir_id)) { + if let Some(Node::Expr(parent_arm_expr)) = map.find_parent(ex.hir_id) { + return match map.find_parent(parent_arm_expr.hir_id) { Some(Node::Local(parent_let_expr)) => Some(AssignmentExpr::Local { span: parent_let_expr.span, pat_span: parent_let_expr.pat.span(), @@ -183,8 +183,7 @@ fn sugg_with_curlies<'a>( // If the parent is already an arm, and the body is another match statement, // we need curly braces around suggestion - let parent_node_id = cx.tcx.hir().parent_id(match_expr.hir_id); - if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { + if let Node::Arm(arm) = &cx.tcx.hir().get_parent(match_expr.hir_id) { if let ExprKind::Match(..) = arm.body.kind { cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the match diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 58c54280a234..1249db5dc479 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().parent_id(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index f96a19b27235..870a1c7d88d5 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -299,7 +299,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().parent_id(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index ac4f8789a434..dd120599c04e 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { return; } let map = &cx.tcx.hir(); - let opt_parent_node = map.find(map.parent_id(expr.hir_id)); + let opt_parent_node = map.find_parent(expr.hir_id); if_chain! { if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; if is_questionmark_desugar_marked_call(parent_expr); @@ -192,7 +192,7 @@ fn fmt_stmts_and_call( let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); // expr is not in a block statement or result expression position, wrap in a block - let parent_node = cx.tcx.hir().find(cx.tcx.hir().parent_id(call_expr.hir_id)); + let parent_node = cx.tcx.hir().find_parent(call_expr.hir_id); if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { let block_indent = call_expr_indent + 4; stmts_and_call_snippet = diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 63f4f01b0877..84ec0d0fb1cf 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } // Abort if the method is implementing a trait or of it a trait method. - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().parent_id(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index a177ae507bbe..c86f24cbd378 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -1058,7 +1058,7 @@ fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) - fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { let map = cx.tcx.hir(); - match map.find(map.parent_id(hir_id)) { + match map.find_parent((hir_id)) { Some(hir::Node::Local(local)) => Some(local), Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id), _ => None, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 63d0938169a8..8290fe9ecb4c 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1287,7 +1287,7 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { /// Gets the parent node, if any. pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { - tcx.hir().parent_iter(id).next().map(|(_, node)| node) + tcx.hir().find_parent(id) } /// Gets the parent expression, if any –- this is useful to constrain a lint. @@ -2075,7 +2075,7 @@ pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { /// } /// ``` pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().parent_id(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) } else { false From ea6ff7ed04b5785d20af03b1e4ac73b4960fa5d5 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Wed, 4 Jan 2023 07:06:07 -0700 Subject: [PATCH 417/524] Apply changes suggested in review --- ...se_sensitive_file_extension_comparisons.rs | 47 ++++++++++--------- ...sensitive_file_extension_comparisons.fixed | 7 +++ ...se_sensitive_file_extension_comparisons.rs | 5 ++ ...ensitive_file_extension_comparisons.stderr | 20 ++++++-- 4 files changed, 54 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index b6ec0fba6cfb..0b3bf22743fa 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; use clippy_utils::source::{indent_of, reindent_multiline}; -use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -19,8 +19,10 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, ) { if let ExprKind::MethodCall(path_segment, ..) = recv.kind { - let method_name = path_segment.ident.name.as_str(); - if method_name == "to_lowercase" || method_name == "to_uppercase" { + if matches!( + path_segment.ident.name.as_str(), + "to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase" + ) { return; } } @@ -45,28 +47,29 @@ pub(super) fn check<'tcx>( "case-sensitive file extension comparison", |diag| { diag.help("consider using a case-insensitive comparison instead"); - let mut recv_source = Sugg::hir(cx, recv, "").to_string(); + if let Some(mut recv_source) = snippet_opt(cx, recv.span) { - if is_type_lang_item(cx, recv_ty, LangItem::String) { - recv_source = format!("&{recv_source}"); - } + if !cx.typeck_results().expr_ty(recv).is_ref() { + recv_source = format!("&{recv_source}"); + } - let suggestion_source = reindent_multiline( - format!( - "std::path::Path::new({}) - .extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", - recv_source, ext_str.strip_prefix('.').unwrap()).into(), - true, - Some(indent_of(cx, call_span).unwrap_or(0) + 4) - ); + let suggestion_source = reindent_multiline( + format!( + "std::path::Path::new({}) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", + recv_source, ext_str.strip_prefix('.').unwrap()).into(), + true, + Some(indent_of(cx, call_span).unwrap_or(0) + 4) + ); - diag.span_suggestion( - recv.span.to(call_span), - "use std::path::Path", - suggestion_source, - Applicability::MaybeIncorrect, - ); + diag.span_suggestion( + recv.span.to(call_span), + "use std::path::Path", + suggestion_source, + Applicability::MaybeIncorrect, + ); + } } ); } diff --git a/tests/ui/case_sensitive_file_extension_comparisons.fixed b/tests/ui/case_sensitive_file_extension_comparisons.fixed index a19ed1ddcd54..5fbaa64db39e 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.fixed +++ b/tests/ui/case_sensitive_file_extension_comparisons.fixed @@ -25,6 +25,13 @@ fn main() { .extension() .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + // The fixup should preserve the indentation level + { + let _ = std::path::Path::new("str") + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + } + // The test struct should not trigger the lint failure with .ext12 TestStruct {}.ends_with(".ext12"); diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs index ad56b7296f75..3c0d4821f9f3 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -19,6 +19,11 @@ fn main() { let _ = String::new().ends_with(".ext12"); let _ = "str".ends_with(".ext12"); + // The fixup should preserve the indentation level + { + let _ = "str".ends_with(".ext12"); + } + // The test struct should not trigger the lint failure with .ext12 TestStruct {}.ends_with(".ext12"); diff --git a/tests/ui/case_sensitive_file_extension_comparisons.stderr b/tests/ui/case_sensitive_file_extension_comparisons.stderr index b5c8e4b4fe68..44c8e3fdf740 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.stderr +++ b/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -42,7 +42,21 @@ LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:26:13 + --> $DIR/case_sensitive_file_extension_comparisons.rs:24:17 + | +LL | let _ = "str".ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new("str") +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + | + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:31:13 | LL | let _ = String::new().ends_with(".EXT12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,7 +70,7 @@ LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:27:13 + --> $DIR/case_sensitive_file_extension_comparisons.rs:32:13 | LL | let _ = "str".ends_with(".EXT12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,5 +83,5 @@ LL + .extension() LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); | -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors From d0c1605d5151c86215ad609feadead811e5602c0 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Wed, 4 Jan 2023 13:32:34 -0800 Subject: [PATCH 418/524] Make the iter_kv_map lint handle ref/mut annotations. For the degenerate (`map(|(k, _)| k)`/`map(|(_, v)| v)`) cases a mut annotation is superfluous and a ref annotation won't compile, so no additional handling is required. For cases where the `map` call must be preserved ref/mut annotations should also be presereved so that the map body continues to work as expected. --- clippy_lints/src/methods/iter_kv_map.rs | 20 +++-- tests/ui/iter_kv_map.fixed | 35 +++++++- tests/ui/iter_kv_map.rs | 39 +++++++- tests/ui/iter_kv_map.stderr | 114 +++++++++++++++++++----- 4 files changed, 174 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 2244ebfb1292..1e5805c53477 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -6,7 +6,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; -use rustc_hir::{BindingAnnotation, Body, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; +use rustc_hir::{BindingAnnotation, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::sym; @@ -30,9 +30,9 @@ pub(super) fn check<'tcx>( if let Body {params: [p], value: body_expr, generator_kind: _ } = cx.tcx.hir().body(c.body); if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind; - let (replacement_kind, binded_ident) = match (&key_pat.kind, &val_pat.kind) { - (key, PatKind::Binding(_, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", value), - (PatKind::Binding(_, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", key), + let (replacement_kind, annotation, binded_ident) = match (&key_pat.kind, &val_pat.kind) { + (key, PatKind::Binding(ann, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", ann, value), + (PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key), _ => return, }; @@ -60,13 +60,23 @@ pub(super) fn check<'tcx>( applicability, ); } else { + let ref_annotation = if annotation.0 == ByRef::Yes { + "ref " + } else { + "" + }; + let mut_annotation = if annotation.1 == Mutability::Mut { + "mut " + } else { + "" + }; span_lint_and_sugg( cx, ITER_KV_MAP, expr.span, &format!("iterating on a map's {replacement_kind}s"), "try", - format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{binded_ident}| {})", + format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{binded_ident}| {})", snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)), applicability, ); diff --git a/tests/ui/iter_kv_map.fixed b/tests/ui/iter_kv_map.fixed index 83fee04080fa..f2a4c284cb16 100644 --- a/tests/ui/iter_kv_map.fixed +++ b/tests/ui/iter_kv_map.fixed @@ -1,14 +1,15 @@ // run-rustfix #![warn(clippy::iter_kv_map)] -#![allow(clippy::redundant_clone)] -#![allow(clippy::suspicious_map)] -#![allow(clippy::map_identity)] +#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] use std::collections::{BTreeMap, HashMap}; fn main() { let get_key = |(key, _val)| key; + fn ref_acceptor(v: &u32) -> u32 { + *v + } let map: HashMap = HashMap::new(); @@ -36,6 +37,20 @@ fn main() { let _ = map.keys().map(|key| key * 9).count(); let _ = map.values().map(|value| value * 17).count(); + // Preserve the ref in the fix. + let _ = map.clone().into_values().map(|ref val| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone().into_values().map(|mut val| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_values().count(); + let map: BTreeMap = BTreeMap::new(); let _ = map.keys().collect::>(); @@ -61,4 +76,18 @@ fn main() { // Lint let _ = map.keys().map(|key| key * 9).count(); let _ = map.values().map(|value| value * 17).count(); + + // Preserve the ref in the fix. + let _ = map.clone().into_values().map(|ref val| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone().into_values().map(|mut val| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_values().count(); } diff --git a/tests/ui/iter_kv_map.rs b/tests/ui/iter_kv_map.rs index 7a1f1fb0198c..ad6564df4084 100644 --- a/tests/ui/iter_kv_map.rs +++ b/tests/ui/iter_kv_map.rs @@ -1,14 +1,15 @@ // run-rustfix #![warn(clippy::iter_kv_map)] -#![allow(clippy::redundant_clone)] -#![allow(clippy::suspicious_map)] -#![allow(clippy::map_identity)] +#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] use std::collections::{BTreeMap, HashMap}; fn main() { let get_key = |(key, _val)| key; + fn ref_acceptor(v: &u32) -> u32 { + *v + } let map: HashMap = HashMap::new(); @@ -36,6 +37,22 @@ fn main() { let _ = map.iter().map(|(key, _value)| key * 9).count(); let _ = map.iter().map(|(_key, value)| value * 17).count(); + // Preserve the ref in the fix. + let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone() + .into_iter() + .map(|(_, mut val)| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); + let map: BTreeMap = BTreeMap::new(); let _ = map.iter().map(|(key, _)| key).collect::>(); @@ -61,4 +78,20 @@ fn main() { // Lint let _ = map.iter().map(|(key, _value)| key * 9).count(); let _ = map.iter().map(|(_key, value)| value * 17).count(); + + // Preserve the ref in the fix. + let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone() + .into_iter() + .map(|(_, mut val)| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); } diff --git a/tests/ui/iter_kv_map.stderr b/tests/ui/iter_kv_map.stderr index 9b9b04c97d81..e00da223b4dd 100644 --- a/tests/ui/iter_kv_map.stderr +++ b/tests/ui/iter_kv_map.stderr @@ -1,5 +1,5 @@ error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:15:13 + --> $DIR/iter_kv_map.rs:16:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` @@ -7,130 +7,198 @@ LL | let _ = map.iter().map(|(key, _)| key).collect::>(); = note: `-D clippy::iter-kv-map` implied by `-D warnings` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:16:13 + --> $DIR/iter_kv_map.rs:17:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:17:13 + --> $DIR/iter_kv_map.rs:18:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:19:13 + --> $DIR/iter_kv_map.rs:20:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:20:13 + --> $DIR/iter_kv_map.rs:21:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:22:13 + --> $DIR/iter_kv_map.rs:23:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:23:13 + --> $DIR/iter_kv_map.rs:24:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:25:13 + --> $DIR/iter_kv_map.rs:26:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:26:13 + --> $DIR/iter_kv_map.rs:27:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:36:13 + --> $DIR/iter_kv_map.rs:37:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:37:13 + --> $DIR/iter_kv_map.rs:38:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` -error: iterating on a map's keys +error: iterating on a map's values --> $DIR/iter_kv_map.rs:41:13 | +LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:44:13 + | +LL | let _ = map + | _____________^ +LL | | .clone() +LL | | .into_iter() +LL | | .map(|(_, mut val)| { +LL | | val += 2; +LL | | val +LL | | }) + | |__________^ + | +help: try + | +LL ~ let _ = map +LL + .clone().into_values().map(|mut val| { +LL + val += 2; +LL + val +LL + }) + | + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:54:13 + | +LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:58:13 + | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:42:13 + --> $DIR/iter_kv_map.rs:59:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:43:13 + --> $DIR/iter_kv_map.rs:60:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:45:13 + --> $DIR/iter_kv_map.rs:62:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:46:13 + --> $DIR/iter_kv_map.rs:63:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:48:13 + --> $DIR/iter_kv_map.rs:65:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:49:13 + --> $DIR/iter_kv_map.rs:66:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:51:13 + --> $DIR/iter_kv_map.rs:68:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:52:13 + --> $DIR/iter_kv_map.rs:69:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:62:13 + --> $DIR/iter_kv_map.rs:79:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:63:13 + --> $DIR/iter_kv_map.rs:80:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` -error: aborting due to 22 previous errors +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:83:13 + | +LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:86:13 + | +LL | let _ = map + | _____________^ +LL | | .clone() +LL | | .into_iter() +LL | | .map(|(_, mut val)| { +LL | | val += 2; +LL | | val +LL | | }) + | |__________^ + | +help: try + | +LL ~ let _ = map +LL + .clone().into_values().map(|mut val| { +LL + val += 2; +LL + val +LL + }) + | + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:96:13 + | +LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` + +error: aborting due to 28 previous errors From 05ba519a3ab8c06ba449d41a14e8584d785c1cb9 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Wed, 4 Jan 2023 11:23:35 +0100 Subject: [PATCH 419/524] trim paths in `default_trait_access`/`clone_on_copy` suggestions --- clippy_lints/src/default.rs | 5 ++--- clippy_lints/src/methods/clone_on_copy.rs | 14 ++++++++------ tests/ui/clone_on_copy.stderr | 2 +- tests/ui/default_trait_access.fixed | 8 ++++---- tests/ui/default_trait_access.stderr | 16 ++++++++-------- tests/ui/unnecessary_clone.stderr | 10 +++++----- 6 files changed, 28 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 55f6121a09d2..a04693f4637a 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -11,6 +11,7 @@ use rustc_hir::def::Res; 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_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -98,9 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { if let ty::Adt(def, ..) = expr_ty.kind(); if !is_from_proc_macro(cx, expr); then { - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did())); + let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did()))); span_lint_and_sugg( cx, DEFAULT_TRAIT_ACCESS, diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 7c7938dd2e8b..3795c0ec2509 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, adjustment::Adjust}; +use rustc_middle::ty::{self, adjustment::Adjust, print::with_forced_trimmed_paths}; use rustc_span::symbol::{sym, Symbol}; use super::CLONE_DOUBLE_REF; @@ -47,10 +47,10 @@ pub(super) fn check( cx, CLONE_DOUBLE_REF, expr.span, - &format!( + &with_forced_trimmed_paths!(format!( "using `clone` on a double-reference; \ this will copy the reference of type `{ty}` instead of cloning the inner type" - ), + )), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; @@ -61,11 +61,11 @@ pub(super) fn check( } let refs = "&".repeat(n + 1); let derefs = "*".repeat(n); - let explicit = format!("<{refs}{ty}>::clone({snip})"); + let explicit = with_forced_trimmed_paths!(format!("<{refs}{ty}>::clone({snip})")); diag.span_suggestion( expr.span, "try dereferencing it", - format!("{refs}({derefs}{}).clone()", snip.deref()), + with_forced_trimmed_paths!(format!("{refs}({derefs}{}).clone()", snip.deref())), Applicability::MaybeIncorrect, ); diag.span_suggestion( @@ -129,7 +129,9 @@ pub(super) fn check( cx, CLONE_ON_COPY, expr.span, - &format!("using `clone` on type `{ty}` which implements the `Copy` trait"), + &with_forced_trimmed_paths!(format!( + "using `clone` on type `{ty}` which implements the `Copy` trait" + )), help, sugg, app, diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index 42ae227777c7..862234d204be 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -48,7 +48,7 @@ error: using `clone` on type `i32` which implements the `Copy` trait LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` -error: using `clone` on type `std::option::Option` which implements the `Copy` trait +error: using `clone` on type `Option` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:77:17 | LL | let value = opt.clone()?; // operator precedence needed (*opt)? diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index eedd43619392..5640599d48ae 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -12,17 +12,17 @@ use std::default::Default as D2; use std::string; fn main() { - let s1: String = std::string::String::default(); + let s1: String = String::default(); let s2 = String::default(); - let s3: String = std::string::String::default(); + let s3: String = String::default(); - let s4: String = std::string::String::default(); + let s4: String = String::default(); let s5 = string::String::default(); - let s6: String = std::string::String::default(); + let s6: String = String::default(); let s7 = std::string::String::default(); diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 49b2dde3f1e8..e4f73c08d190 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,8 +1,8 @@ -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:15:22 | LL | let s1: String = Default::default(); - | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^^^^^^ help: try: `String::default()` | note: the lint level is defined here --> $DIR/default_trait_access.rs:3:9 @@ -10,23 +10,23 @@ note: the lint level is defined here LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:19:22 | LL | let s3: String = D2::default(); - | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^ help: try: `String::default()` -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:21:22 | LL | let s4: String = std::default::Default::default(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:25:22 | LL | let s6: String = default::Default::default(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `GenericDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:35:46 diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 94cc7777acac..6022d9fa4c5c 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -38,13 +38,13 @@ LL | t.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on type `std::option::Option` which implements the `Copy` trait +error: using `clone` on type `Option` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:42:5 | LL | Some(t).clone(); | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` -error: using `clone` on a double-reference; this will copy the reference of type `&std::vec::Vec` instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&Vec` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:48:22 | LL | let z: &Vec<_> = y.clone(); @@ -57,10 +57,10 @@ LL | let z: &Vec<_> = &(*y).clone(); | ~~~~~~~~~~~~~ help: or try being explicit if you are sure, that you want to clone a reference | -LL | let z: &Vec<_> = <&std::vec::Vec>::clone(y); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let z: &Vec<_> = <&Vec>::clone(y); + | ~~~~~~~~~~~~~~~~~~~~~ -error: using `clone` on type `many_derefs::E` which implements the `Copy` trait +error: using `clone` on type `E` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:84:20 | LL | let _: E = a.clone(); From 755ae3fa29b0b2c6b11eaecf81b201b0a9a026bf Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Wed, 4 Jan 2023 14:58:07 -0800 Subject: [PATCH 420/524] Fix spelling while we're in the neighborhood. --- clippy_lints/src/methods/iter_kv_map.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 1e5805c53477..c87f5daab6f2 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( if let Body {params: [p], value: body_expr, generator_kind: _ } = cx.tcx.hir().body(c.body); if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind; - let (replacement_kind, annotation, binded_ident) = match (&key_pat.kind, &val_pat.kind) { + let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) { (key, PatKind::Binding(ann, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", ann, value), (PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key), _ => return, @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( if_chain! { if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind; if let [local_ident] = path.segments; - if local_ident.ident.as_str() == binded_ident.as_str(); + if local_ident.ident.as_str() == bound_ident.as_str(); then { span_lint_and_sugg( @@ -76,7 +76,7 @@ pub(super) fn check<'tcx>( expr.span, &format!("iterating on a map's {replacement_kind}s"), "try", - format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{binded_ident}| {})", + format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})", snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)), applicability, ); From 1c42dbba60ece87f3031bc2dad5497f3cc9ad7d0 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Wed, 4 Jan 2023 19:08:37 -0800 Subject: [PATCH 421/524] Expand derivable-impls to cover enums with a default unit variant. --- clippy_lints/src/derivable_impls.rs | 143 ++++++++++++++++++++-------- tests/ui/derivable_impls.fixed | 21 ++++ tests/ui/derivable_impls.rs | 23 +++++ tests/ui/derivable_impls.stderr | 23 ++++- 4 files changed, 167 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index ae8f6b794499..c987fdb1a679 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::indent_of; use clippy_utils::{is_default_equivalent, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{ - def::{DefKind, Res}, - Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind, + def::{CtorKind, CtorOf, DefKind, Res}, + Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{AdtDef, DefIdTree}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -61,6 +63,98 @@ fn is_path_self(e: &Expr<'_>) -> bool { } } +fn check_struct<'tcx>( + cx: &LateContext<'tcx>, + item: &'tcx Item<'_>, + self_ty: &Ty<'_>, + func_expr: &Expr<'_>, + adt_def: AdtDef<'_>, +) { + if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { + if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() { + for arg in a.args { + if !matches!(arg, GenericArg::Lifetime(_)) { + return; + } + } + } + } + let should_emit = match peel_blocks(func_expr).kind { + ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)), + ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)), + ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)), + _ => false, + }; + + if should_emit { + let struct_span = cx.tcx.def_span(adt_def.did()); + span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| { + diag.span_suggestion_hidden( + item.span, + "remove the manual implementation...", + String::new(), + Applicability::MachineApplicable, + ); + diag.span_suggestion( + struct_span.shrink_to_lo(), + "...and instead derive it", + "#[derive(Default)]\n".to_string(), + Applicability::MachineApplicable, + ); + }); + } +} + +fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) { + if_chain! { + if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind; + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res; + if let variant_id = cx.tcx.parent(id); + if let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id); + if variant_def.fields.is_empty(); + if !variant_def.is_field_list_non_exhaustive(); + + then { + let enum_span = cx.tcx.def_span(adt_def.did()); + let indent_enum = indent_of(cx, enum_span).unwrap_or(0); + let variant_span = cx.tcx.def_span(variant_def.def_id); + let indent_variant = indent_of(cx, variant_span).unwrap_or(0); + span_lint_and_then( + cx, + DERIVABLE_IMPLS, + item.span, + "this `impl` can be derived", + |diag| { + diag.span_suggestion_hidden( + item.span, + "remove the manual implementation...", + String::new(), + Applicability::MachineApplicable + ); + diag.span_suggestion( + enum_span.shrink_to_lo(), + "...and instead derive it...", + format!( + "#[derive(Default)]\n{indent}", + indent = " ".repeat(indent_enum), + ), + Applicability::MachineApplicable + ); + diag.span_suggestion( + variant_span.shrink_to_lo(), + "...and mark the default variant", + format!( + "#[default]\n{indent}", + indent = " ".repeat(indent_variant), + ), + Applicability::MachineApplicable + ); + } + ); + } + } +} + impl<'tcx> LateLintPass<'tcx> for DerivableImpls { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { @@ -83,47 +177,12 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if !attrs.iter().any(|attr| attr.doc_str().is_some()); if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); if !child_attrs.iter().any(|attr| attr.doc_str().is_some()); - if adt_def.is_struct(); - then { - if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { - if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() { - for arg in a.args { - if !matches!(arg, GenericArg::Lifetime(_)) { - return; - } - } - } - } - let should_emit = match peel_blocks(func_expr).kind { - ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)), - ExprKind::Call(callee, args) - if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)), - ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)), - _ => false, - }; - if should_emit { - let struct_span = cx.tcx.def_span(adt_def.did()); - span_lint_and_then( - cx, - DERIVABLE_IMPLS, - item.span, - "this `impl` can be derived", - |diag| { - diag.span_suggestion_hidden( - item.span, - "remove the manual implementation...", - String::new(), - Applicability::MachineApplicable - ); - diag.span_suggestion( - struct_span.shrink_to_lo(), - "...and instead derive it", - "#[derive(Default)]\n".to_string(), - Applicability::MachineApplicable - ); - } - ); + then { + if adt_def.is_struct() { + check_struct(cx, item, self_ty, func_expr, adt_def); + } else if adt_def.is_enum() { + check_enum(cx, item, func_expr, adt_def); } } } diff --git a/tests/ui/derivable_impls.fixed b/tests/ui/derivable_impls.fixed index 7dcdfb0937e8..ee8456f5deb8 100644 --- a/tests/ui/derivable_impls.fixed +++ b/tests/ui/derivable_impls.fixed @@ -210,4 +210,25 @@ impl Default for IntOrString { } } +#[derive(Default)] +pub enum SimpleEnum { + Foo, + #[default] + Bar, +} + + + +pub enum NonExhaustiveEnum { + Foo, + #[non_exhaustive] + Bar, +} + +impl Default for NonExhaustiveEnum { + fn default() -> Self { + NonExhaustiveEnum::Bar + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs index 625cbcdde230..14af419bcad1 100644 --- a/tests/ui/derivable_impls.rs +++ b/tests/ui/derivable_impls.rs @@ -244,4 +244,27 @@ impl Default for IntOrString { } } +pub enum SimpleEnum { + Foo, + Bar, +} + +impl Default for SimpleEnum { + fn default() -> Self { + SimpleEnum::Bar + } +} + +pub enum NonExhaustiveEnum { + Foo, + #[non_exhaustive] + Bar, +} + +impl Default for NonExhaustiveEnum { + fn default() -> Self { + NonExhaustiveEnum::Bar + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.stderr b/tests/ui/derivable_impls.stderr index c1db5a58b1f5..81963c3be5b5 100644 --- a/tests/ui/derivable_impls.stderr +++ b/tests/ui/derivable_impls.stderr @@ -113,5 +113,26 @@ help: ...and instead derive it LL | #[derive(Default)] | -error: aborting due to 7 previous errors +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:252:1 + | +LL | / impl Default for SimpleEnum { +LL | | fn default() -> Self { +LL | | SimpleEnum::Bar +LL | | } +LL | | } + | |_^ + | + = help: remove the manual implementation... +help: ...and instead derive it... + | +LL | #[derive(Default)] + | +help: ...and mark the default variant + | +LL ~ #[default] +LL ~ Bar, + | + +error: aborting due to 8 previous errors From 79ed23ff815bc39d0062ade40b2c38dd37e77373 Mon Sep 17 00:00:00 2001 From: Raiki Tamura Date: Thu, 5 Jan 2023 18:30:13 +0900 Subject: [PATCH 422/524] fix --- clippy_lints/src/loops/single_element_loop.rs | 14 +++++++++ tests/ui/single_element_loop.fixed | 27 +++++++++++++++++ tests/ui/single_element_loop.rs | 26 +++++++++++++++++ tests/ui/single_element_loop.stderr | 29 ++++++++++++++++++- 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index f4b47808dfaa..7cbc456244e4 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -1,6 +1,7 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet_with_applicability}; +use clippy_utils::visitors::for_each_expr; use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::Mutability; @@ -8,6 +9,18 @@ use rustc_errors::Applicability; use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat}; use rustc_lint::LateContext; use rustc_span::edition::Edition; +use std::ops::ControlFlow; + +fn contains_break_or_continue(expr: &Expr<'_>) -> bool { + for_each_expr(expr, |e| { + if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -67,6 +80,7 @@ pub(super) fn check<'tcx>( if_chain! { if let ExprKind::Block(block, _) = body.kind; if !block.stmts.is_empty(); + if !contains_break_or_continue(body); then { let mut applicability = Applicability::MachineApplicable; let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed index 63d31ff83f9b..a0dcc0172e8b 100644 --- a/tests/ui/single_element_loop.fixed +++ b/tests/ui/single_element_loop.fixed @@ -33,4 +33,31 @@ fn main() { let item = 0..5; dbg!(item); } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + continue; + } + } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + break; + } + } + + // should lint (issue #10018) + { + let _ = 42; + let _f = |n: u32| { + for i in 0..n { + if i > 10 { + dbg!(i); + break; + } + } + }; + } } diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs index 2cda5a329d25..bc014035c98a 100644 --- a/tests/ui/single_element_loop.rs +++ b/tests/ui/single_element_loop.rs @@ -27,4 +27,30 @@ fn main() { for item in [0..5].into_iter() { dbg!(item); } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + continue; + } + } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + break; + } + } + + // should lint (issue #10018) + for _ in [42] { + let _f = |n: u32| { + for i in 0..n { + if i > 10 { + dbg!(i); + break; + } + } + }; + } } diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr index 0aeb8da1a2e2..14437a59745e 100644 --- a/tests/ui/single_element_loop.stderr +++ b/tests/ui/single_element_loop.stderr @@ -95,5 +95,32 @@ LL + dbg!(item); LL + } | -error: aborting due to 6 previous errors +error: for loop over a single element + --> $DIR/single_element_loop.rs:46:5 + | +LL | / for _ in [42] { +LL | | let _f = |n: u32| { +LL | | for i in 0..n { +LL | | if i > 10 { +... | +LL | | }; +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let _ = 42; +LL + let _f = |n: u32| { +LL + for i in 0..n { +LL + if i > 10 { +LL + dbg!(i); +LL + break; +LL + } +LL + } +LL + }; +LL + } + | + +error: aborting due to 7 previous errors From 6433d796a1e22ea37f9b51bda5248b9ac82df578 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Thu, 5 Jan 2023 13:06:43 -0800 Subject: [PATCH 423/524] Restrict suggestion of deriving Default for enums to MSRV 1.62. See https://blog.rust-lang.org/2022/06/30/Rust-1.62.0.html#default-enum-variants --- clippy_lints/src/derivable_impls.rs | 20 +++++++++++++++++--- clippy_lints/src/lib.rs | 2 +- clippy_utils/src/msrvs.rs | 4 ++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index c987fdb1a679..bc18e2e5ed5f 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::indent_of; use clippy_utils::{is_default_equivalent, peel_blocks}; use rustc_errors::Applicability; @@ -8,7 +9,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{AdtDef, DefIdTree}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; declare_clippy_lint! { @@ -53,7 +54,18 @@ declare_clippy_lint! { "manual implementation of the `Default` trait which is equal to a derive" } -declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); +pub struct DerivableImpls { + msrv: Msrv, +} + +impl DerivableImpls { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + DerivableImpls { msrv } + } +} + +impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); fn is_path_self(e: &Expr<'_>) -> bool { if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind { @@ -181,10 +193,12 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { then { if adt_def.is_struct() { check_struct(cx, item, self_ty, func_expr, adt_def); - } else if adt_def.is_enum() { + } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { check_enum(cx, item, func_expr, adt_def); } } } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dcd8ca81ae87..d8e2ae02c5a6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -639,7 +639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented)); store.register_late_pass(|_| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|_| Box::new(derive::Derive)); - store.register_late_pass(|_| Box::new(derivable_impls::DerivableImpls)); + store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(msrv()))); store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index ba5bc9c3135d..dbf9f3b621d7 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -20,8 +20,9 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,65,0 { LET_ELSE } - 1,62,0 { BOOL_THEN_SOME } + 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,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 } @@ -45,7 +46,6 @@ msrv_aliases! { 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } - 1,55,0 { SEEK_REWIND } } fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { From e443604a24029e0ffd77f917e82980539fdacbbe Mon Sep 17 00:00:00 2001 From: Robin Schroer Date: Fri, 6 Jan 2023 12:52:51 +0900 Subject: [PATCH 424/524] unused_self: Don't trigger if the method body contains todo!() If the author is using todo!(), presumably they intend to use self at some point later, so we don't have a good basis to recommend factoring out to an associated function. Fixes #10117. changelog: Don't trigger [`unused_self`] if the method body contains a `todo!()` call --- clippy_lints/src/unused_self.rs | 19 ++++++++++++++++++- tests/ui/unused_self.rs | 10 ++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 18231b6a7e86..f864c520302e 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::visitors::is_local_used; use if_chain::if_chain; -use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind}; +use rustc_hir::{Body, Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -57,6 +59,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id; let parent_item = cx.tcx.hir().expect_item(parent); let assoc_item = cx.tcx.associated_item(impl_item.owner_id); + let contains_todo = |cx, body: &'_ Body<'_>| -> bool { + clippy_utils::visitors::for_each_expr(body.value, |e| { + if let Some(macro_call) = root_macro_call_first_node(cx, e) { + if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } else { + ControlFlow::Continue(()) + } + }) + .is_some() + }; if_chain! { if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind; if assoc_item.fn_has_self_parameter; @@ -65,6 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { let body = cx.tcx.hir().body(*body_id); if let [self_param, ..] = body.params; if !is_local_used(cx, body, self_param.pat.hir_id); + if !contains_todo(cx, body); then { span_lint_and_help( cx, diff --git a/tests/ui/unused_self.rs b/tests/ui/unused_self.rs index 92e8e1dba69d..55bd5607185c 100644 --- a/tests/ui/unused_self.rs +++ b/tests/ui/unused_self.rs @@ -60,6 +60,16 @@ mod unused_self_allow { // shouldn't trigger for public methods pub fn unused_self_move(self) {} } + + pub struct E; + + impl E { + // shouldn't trigger if body contains todo!() + pub fn unused_self_todo(self) { + let x = 42; + todo!() + } + } } pub use unused_self_allow::D; From 1b9a25e28d76221ee9906b2c3ec833a9837d0349 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Fri, 6 Jan 2023 14:41:50 +0100 Subject: [PATCH 425/524] [#10167] Clarify that the lint only works if x eq. y in a `for` loop. Reading the documentation for the lint, one could expect that the lint works in all cases that `X == Y`. This is false. While the lint was updated, the documentation wasn't. More information about the `N..N` problem in #5689 and #5628 --- clippy_lints/src/ranges.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 0a1b9d173cf9..fc655fe2d0bb 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -103,7 +103,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for range expressions `x..y` where both `x` and `y` - /// are constant and `x` is greater or equal to `y`. + /// are constant and `x` is greater to `y`. Also triggers if `x` is equal to `y` when they are conditions to a `for` loop. /// /// ### Why is this bad? /// Empty ranges yield no values so iterating them is a no-op. From 4262aebeaaa5789279110b1f039e991ad8996fa2 Mon Sep 17 00:00:00 2001 From: Caio Date: Fri, 6 Jan 2023 12:25:51 -0300 Subject: [PATCH 426/524] [arithmetic-side-effects] Consider negative numbers and add more tests --- .../src/operators/arithmetic_side_effects.rs | 11 +- clippy_utils/src/lib.rs | 12 + .../arithmetic_side_effects_allowed.rs | 2 +- tests/ui/arithmetic_side_effects.rs | 67 ++++- tests/ui/arithmetic_side_effects.stderr | 274 ++++++++++++++---- 5 files changed, 304 insertions(+), 62 deletions(-) diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 4fbc8398e373..cff82b875f11 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -2,7 +2,7 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_utils::{ consts::{constant, constant_simple}, diagnostics::span_lint, - peel_hir_expr_refs, + peel_hir_expr_refs, peel_hir_expr_unary, }; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -98,8 +98,11 @@ impl ArithmeticSideEffects { } /// If `expr` is not a literal integer like `1`, returns `None`. + /// + /// Returns the absolute value of the expression, if this is an integer literal. fn literal_integer(expr: &hir::Expr<'_>) -> Option { - if let hir::ExprKind::Lit(ref lit) = expr.kind && let ast::LitKind::Int(n, _) = lit.node { + let actual = peel_hir_expr_unary(expr).0; + if let hir::ExprKind::Lit(ref lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node { Some(n) } else { @@ -123,12 +126,12 @@ impl ArithmeticSideEffects { if !matches!( op.node, hir::BinOpKind::Add - | hir::BinOpKind::Sub - | hir::BinOpKind::Mul | hir::BinOpKind::Div + | hir::BinOpKind::Mul | hir::BinOpKind::Rem | hir::BinOpKind::Shl | hir::BinOpKind::Shr + | hir::BinOpKind::Sub ) { return; }; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e7000fb50586..1dc68be31d92 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2258,6 +2258,18 @@ pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<' (e, count - remaining) } +/// Peels off all unary operators of an expression. Returns the underlying expression and the number +/// of operators removed. +pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { + let mut count: usize = 0; + let mut curr_expr = expr; + while let ExprKind::Unary(_, local_expr) = curr_expr.kind { + count = count.wrapping_add(1); + curr_expr = local_expr; + } + (curr_expr, count) +} + /// Peels off all references on the expression. Returns the underlying expression and the number of /// references removed. pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs index 36db9e54a228..fb5b1b193f84 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs @@ -107,7 +107,7 @@ fn rhs_is_different() { fn unary() { // is explicitly on the list let _ = -OutOfNames; - // is specifically on the list + // is explicitly on the list let _ = -Foo; // not on the list let _ = -Bar; diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index b5ed8988a518..66c468c94063 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -2,6 +2,7 @@ clippy::assign_op_pattern, clippy::erasing_op, clippy::identity_op, + clippy::no_effect, clippy::op_ref, clippy::unnecessary_owned_empty_strings, arithmetic_overflow, @@ -125,6 +126,18 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n *= &0; _n *= 1; _n *= &1; + _n += -0; + _n += &-0; + _n -= -0; + _n -= &-0; + _n /= -99; + _n /= &-99; + _n %= -99; + _n %= &-99; + _n *= -0; + _n *= &-0; + _n *= -1; + _n *= &-1; // Binary _n = _n + 0; @@ -158,7 +171,7 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n = -&i32::MIN; } -pub fn runtime_ops() { +pub fn unknown_ops_or_runtime_ops_that_can_overflow() { let mut _n = i32::MAX; // Assign @@ -172,6 +185,16 @@ pub fn runtime_ops() { _n %= &0; _n *= 2; _n *= &2; + _n += -1; + _n += &-1; + _n -= -1; + _n -= &-1; + _n /= -0; + _n /= &-0; + _n %= -0; + _n %= &-0; + _n *= -2; + _n *= &-2; // Binary _n = _n + 1; @@ -225,4 +248,46 @@ pub fn runtime_ops() { _n = -&_n; } +// Copied and pasted from the `integer_arithmetic` lint for comparison. +pub fn integer_arithmetic() { + let mut i = 1i32; + let mut var1 = 0i32; + let mut var2 = -1i32; + + 1 + i; + i * 2; + 1 % i / 2; + i - 2 + 2 - i; + -i; + i >> 1; + i << 1; + + -1; + -(-1); + + i & 1; + i | 1; + i ^ 1; + + i += 1; + i -= 1; + i *= 2; + i /= 2; + i /= 0; + i /= -1; + i /= var1; + i /= var2; + i %= 2; + i %= 0; + i %= -1; + i %= var1; + i %= var2; + i <<= 3; + i >>= 2; + + i |= 1; + i &= 1; + i ^= i; +} + fn main() {} diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 9fe4b7cf28d8..a9bb0ae9280e 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,5 +1,5 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:165:5 + --> $DIR/arithmetic_side_effects.rs:178:5 | LL | _n += 1; | ^^^^^^^ @@ -7,328 +7,490 @@ LL | _n += 1; = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:166:5 + --> $DIR/arithmetic_side_effects.rs:179:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:167:5 + --> $DIR/arithmetic_side_effects.rs:180:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:168:5 + --> $DIR/arithmetic_side_effects.rs:181:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:169:5 + --> $DIR/arithmetic_side_effects.rs:182:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:170:5 + --> $DIR/arithmetic_side_effects.rs:183:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:171:5 + --> $DIR/arithmetic_side_effects.rs:184:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:172:5 + --> $DIR/arithmetic_side_effects.rs:185:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:173:5 + --> $DIR/arithmetic_side_effects.rs:186:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:174:5 + --> $DIR/arithmetic_side_effects.rs:187:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:177:10 + --> $DIR/arithmetic_side_effects.rs:188:5 + | +LL | _n += -1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:189:5 + | +LL | _n += &-1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:190:5 + | +LL | _n -= -1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:191:5 + | +LL | _n -= &-1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:192:5 + | +LL | _n /= -0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:193:5 + | +LL | _n /= &-0; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:194:5 + | +LL | _n %= -0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:195:5 + | +LL | _n %= &-0; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:196:5 + | +LL | _n *= -2; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:197:5 + | +LL | _n *= &-2; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:200:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:178:10 + --> $DIR/arithmetic_side_effects.rs:201:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:179:10 + --> $DIR/arithmetic_side_effects.rs:202:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:180:10 + --> $DIR/arithmetic_side_effects.rs:203:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:181:10 + --> $DIR/arithmetic_side_effects.rs:204:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:182:10 + --> $DIR/arithmetic_side_effects.rs:205:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:183:10 + --> $DIR/arithmetic_side_effects.rs:206:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:184:10 + --> $DIR/arithmetic_side_effects.rs:207:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:185:10 + --> $DIR/arithmetic_side_effects.rs:208:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:186:10 + --> $DIR/arithmetic_side_effects.rs:209:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:187:10 + --> $DIR/arithmetic_side_effects.rs:210:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:188:10 + --> $DIR/arithmetic_side_effects.rs:211:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:189:10 + --> $DIR/arithmetic_side_effects.rs:212:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:190:10 + --> $DIR/arithmetic_side_effects.rs:213:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:191:10 + --> $DIR/arithmetic_side_effects.rs:214:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:192:10 + --> $DIR/arithmetic_side_effects.rs:215:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:193:10 + --> $DIR/arithmetic_side_effects.rs:216:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:194:10 + --> $DIR/arithmetic_side_effects.rs:217:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:195:10 + --> $DIR/arithmetic_side_effects.rs:218:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:198:13 + --> $DIR/arithmetic_side_effects.rs:221:13 | LL | let _ = Custom + 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:199:13 + --> $DIR/arithmetic_side_effects.rs:222:13 | LL | let _ = Custom + 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:200:13 + --> $DIR/arithmetic_side_effects.rs:223:13 | LL | let _ = Custom + 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:201:13 + --> $DIR/arithmetic_side_effects.rs:224:13 | LL | let _ = Custom + 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:202:13 + --> $DIR/arithmetic_side_effects.rs:225:13 | LL | let _ = Custom + 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:203:13 + --> $DIR/arithmetic_side_effects.rs:226:13 | LL | let _ = Custom + 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:204:13 + --> $DIR/arithmetic_side_effects.rs:227:13 | LL | let _ = Custom - 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:205:13 + --> $DIR/arithmetic_side_effects.rs:228:13 | LL | let _ = Custom - 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:206:13 + --> $DIR/arithmetic_side_effects.rs:229:13 | LL | let _ = Custom - 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:207:13 + --> $DIR/arithmetic_side_effects.rs:230:13 | LL | let _ = Custom - 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:208:13 + --> $DIR/arithmetic_side_effects.rs:231:13 | LL | let _ = Custom - 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:209:13 + --> $DIR/arithmetic_side_effects.rs:232:13 | LL | let _ = Custom - 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:210:13 + --> $DIR/arithmetic_side_effects.rs:233:13 | LL | let _ = Custom / 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:211:13 + --> $DIR/arithmetic_side_effects.rs:234:13 | LL | let _ = Custom / 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:212:13 + --> $DIR/arithmetic_side_effects.rs:235:13 | LL | let _ = Custom / 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:213:13 + --> $DIR/arithmetic_side_effects.rs:236:13 | LL | let _ = Custom / 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:214:13 + --> $DIR/arithmetic_side_effects.rs:237:13 | LL | let _ = Custom / 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:215:13 + --> $DIR/arithmetic_side_effects.rs:238:13 | LL | let _ = Custom / 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:216:13 + --> $DIR/arithmetic_side_effects.rs:239:13 | LL | let _ = Custom * 0; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:217:13 + --> $DIR/arithmetic_side_effects.rs:240:13 | LL | let _ = Custom * 1; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:218:13 + --> $DIR/arithmetic_side_effects.rs:241:13 | LL | let _ = Custom * 2; | ^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:219:13 + --> $DIR/arithmetic_side_effects.rs:242:13 | LL | let _ = Custom * 0.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:220:13 + --> $DIR/arithmetic_side_effects.rs:243:13 | LL | let _ = Custom * 1.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:221:13 + --> $DIR/arithmetic_side_effects.rs:244:13 | LL | let _ = Custom * 2.0; | ^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:224:10 + --> $DIR/arithmetic_side_effects.rs:247:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:225:10 + --> $DIR/arithmetic_side_effects.rs:248:10 | LL | _n = -&_n; | ^^^^ -error: aborting due to 55 previous errors +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:257:5 + | +LL | 1 + i; + | ^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:258:5 + | +LL | i * 2; + | ^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:260:5 + | +LL | i - 2 + 2 - i; + | ^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:261:5 + | +LL | -i; + | ^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:262:5 + | +LL | i >> 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:263:5 + | +LL | i << 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:272:5 + | +LL | i += 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:273:5 + | +LL | i -= 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:274:5 + | +LL | i *= 2; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:276:5 + | +LL | i /= 0; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:278:5 + | +LL | i /= var1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:279:5 + | +LL | i /= var2; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:281:5 + | +LL | i %= 0; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:283:5 + | +LL | i %= var1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:284:5 + | +LL | i %= var2; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:285:5 + | +LL | i <<= 3; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:286:5 + | +LL | i >>= 2; + | ^^^^^^^ + +error: aborting due to 82 previous errors From fb34fbf7e01d2cd9b44d762d4cd17d456796a63a Mon Sep 17 00:00:00 2001 From: Caio Date: Fri, 6 Jan 2023 14:50:25 -0300 Subject: [PATCH 427/524] [arithmetic_side_effects] Add more tests related to custom types --- tests/ui/arithmetic_side_effects.rs | 158 +++++++--- tests/ui/arithmetic_side_effects.stderr | 390 +++++++++++++++--------- 2 files changed, 365 insertions(+), 183 deletions(-) diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index 66c468c94063..918cf81c600a 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -13,31 +13,95 @@ use core::num::{Saturating, Wrapping}; +#[derive(Clone, Copy)] pub struct Custom; macro_rules! impl_arith { - ( $( $_trait:ident, $ty:ty, $method:ident; )* ) => { + ( $( $_trait:ident, $lhs:ty, $rhs:ty, $method:ident; )* ) => { $( - impl core::ops::$_trait<$ty> for Custom { - type Output = Self; - fn $method(self, _: $ty) -> Self::Output { Self } + impl core::ops::$_trait<$lhs> for $rhs { + type Output = Custom; + fn $method(self, _: $lhs) -> Self::Output { todo!() } + } + )* + } +} + +macro_rules! impl_assign_arith { + ( $( $_trait:ident, $lhs:ty, $rhs:ty, $method:ident; )* ) => { + $( + impl core::ops::$_trait<$lhs> for $rhs { + fn $method(&mut self, _: $lhs) {} } )* } } impl_arith!( - Add, i32, add; - Div, i32, div; - Mul, i32, mul; - Sub, i32, sub; - - Add, f64, add; - Div, f64, div; - Mul, f64, mul; - Sub, f64, sub; + Add, Custom, Custom, add; + Div, Custom, Custom, div; + Mul, Custom, Custom, mul; + Rem, Custom, Custom, rem; + Sub, Custom, Custom, sub; + + Add, Custom, &Custom, add; + Div, Custom, &Custom, div; + Mul, Custom, &Custom, mul; + Rem, Custom, &Custom, rem; + Sub, Custom, &Custom, sub; + + Add, &Custom, Custom, add; + Div, &Custom, Custom, div; + Mul, &Custom, Custom, mul; + Rem, &Custom, Custom, rem; + Sub, &Custom, Custom, sub; + + Add, &Custom, &Custom, add; + Div, &Custom, &Custom, div; + Mul, &Custom, &Custom, mul; + Rem, &Custom, &Custom, rem; + Sub, &Custom, &Custom, sub; +); + +impl_assign_arith!( + AddAssign, Custom, Custom, add_assign; + DivAssign, Custom, Custom, div_assign; + MulAssign, Custom, Custom, mul_assign; + RemAssign, Custom, Custom, rem_assign; + SubAssign, Custom, Custom, sub_assign; + + AddAssign, Custom, &Custom, add_assign; + DivAssign, Custom, &Custom, div_assign; + MulAssign, Custom, &Custom, mul_assign; + RemAssign, Custom, &Custom, rem_assign; + SubAssign, Custom, &Custom, sub_assign; + + AddAssign, &Custom, Custom, add_assign; + DivAssign, &Custom, Custom, div_assign; + MulAssign, &Custom, Custom, mul_assign; + RemAssign, &Custom, Custom, rem_assign; + SubAssign, &Custom, Custom, sub_assign; + + AddAssign, &Custom, &Custom, add_assign; + DivAssign, &Custom, &Custom, div_assign; + MulAssign, &Custom, &Custom, mul_assign; + RemAssign, &Custom, &Custom, rem_assign; + SubAssign, &Custom, &Custom, sub_assign; ); +impl core::ops::Neg for Custom { + type Output = Custom; + fn neg(self) -> Self::Output { + todo!() + } +} +impl core::ops::Neg for &Custom { + type Output = Custom; + fn neg(self) -> Self::Output { + todo!() + } +} + pub fn association_with_structures_should_not_trigger_the_lint() { enum Foo { Bar = -2, @@ -173,6 +237,7 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri pub fn unknown_ops_or_runtime_ops_that_can_overflow() { let mut _n = i32::MAX; + let mut _custom = Custom; // Assign _n += 1; @@ -195,6 +260,26 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _n %= &-0; _n *= -2; _n *= &-2; + _custom += Custom; + _custom += &Custom; + _custom -= Custom; + _custom -= &Custom; + _custom /= Custom; + _custom /= &Custom; + _custom %= Custom; + _custom %= &Custom; + _custom *= Custom; + _custom *= &Custom; + _custom += -Custom; + _custom += &-Custom; + _custom -= -Custom; + _custom -= &-Custom; + _custom /= -Custom; + _custom /= &-Custom; + _custom %= -Custom; + _custom %= &-Custom; + _custom *= -Custom; + _custom *= &-Custom; // Binary _n = _n + 1; @@ -216,36 +301,31 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _n = 23 + &85; _n = &23 + 85; _n = &23 + &85; - - // Custom - let _ = Custom + 0; - let _ = Custom + 1; - let _ = Custom + 2; - let _ = Custom + 0.0; - let _ = Custom + 1.0; - let _ = Custom + 2.0; - let _ = Custom - 0; - let _ = Custom - 1; - let _ = Custom - 2; - let _ = Custom - 0.0; - let _ = Custom - 1.0; - let _ = Custom - 2.0; - let _ = Custom / 0; - let _ = Custom / 1; - let _ = Custom / 2; - let _ = Custom / 0.0; - let _ = Custom / 1.0; - let _ = Custom / 2.0; - let _ = Custom * 0; - let _ = Custom * 1; - let _ = Custom * 2; - let _ = Custom * 0.0; - let _ = Custom * 1.0; - let _ = Custom * 2.0; + _custom = _custom + _custom; + _custom = _custom + &_custom; + _custom = Custom + _custom; + _custom = &Custom + _custom; + _custom = _custom - Custom; + _custom = _custom - &Custom; + _custom = Custom - _custom; + _custom = &Custom - _custom; + _custom = _custom / Custom; + _custom = _custom / &Custom; + _custom = _custom % Custom; + _custom = _custom % &Custom; + _custom = _custom * Custom; + _custom = _custom * &Custom; + _custom = Custom * _custom; + _custom = &Custom * _custom; + _custom = Custom + &Custom; + _custom = &Custom + Custom; + _custom = &Custom + &Custom; // Unary _n = -_n; _n = -&_n; + _custom = -_custom; + _custom = -&_custom; } // Copied and pasted from the `integer_arithmetic` lint for comparison. diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index a9bb0ae9280e..5e349f6b497c 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,5 +1,5 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:178:5 + --> $DIR/arithmetic_side_effects.rs:243:5 | LL | _n += 1; | ^^^^^^^ @@ -7,490 +7,592 @@ LL | _n += 1; = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:179:5 + --> $DIR/arithmetic_side_effects.rs:244:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:180:5 + --> $DIR/arithmetic_side_effects.rs:245:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:181:5 + --> $DIR/arithmetic_side_effects.rs:246:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:182:5 + --> $DIR/arithmetic_side_effects.rs:247:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:183:5 + --> $DIR/arithmetic_side_effects.rs:248:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:184:5 + --> $DIR/arithmetic_side_effects.rs:249:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:185:5 + --> $DIR/arithmetic_side_effects.rs:250:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:186:5 + --> $DIR/arithmetic_side_effects.rs:251:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:187:5 + --> $DIR/arithmetic_side_effects.rs:252:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:188:5 + --> $DIR/arithmetic_side_effects.rs:253:5 | LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:189:5 + --> $DIR/arithmetic_side_effects.rs:254:5 | LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:190:5 + --> $DIR/arithmetic_side_effects.rs:255:5 | LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:191:5 + --> $DIR/arithmetic_side_effects.rs:256:5 | LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:192:5 + --> $DIR/arithmetic_side_effects.rs:257:5 | LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:193:5 + --> $DIR/arithmetic_side_effects.rs:258:5 | LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:194:5 + --> $DIR/arithmetic_side_effects.rs:259:5 | LL | _n %= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:195:5 + --> $DIR/arithmetic_side_effects.rs:260:5 | LL | _n %= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:196:5 + --> $DIR/arithmetic_side_effects.rs:261:5 | LL | _n *= -2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:197:5 + --> $DIR/arithmetic_side_effects.rs:262:5 | LL | _n *= &-2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:200:10 + --> $DIR/arithmetic_side_effects.rs:263:5 + | +LL | _custom += Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:264:5 + | +LL | _custom += &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:265:5 + | +LL | _custom -= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:266:5 + | +LL | _custom -= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:267:5 + | +LL | _custom /= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:268:5 + | +LL | _custom /= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:269:5 + | +LL | _custom %= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:270:5 + | +LL | _custom %= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:271:5 + | +LL | _custom *= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:272:5 + | +LL | _custom *= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:273:5 + | +LL | _custom += -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:274:5 + | +LL | _custom += &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:275:5 + | +LL | _custom -= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:276:5 + | +LL | _custom -= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:277:5 + | +LL | _custom /= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:278:5 + | +LL | _custom /= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:279:5 + | +LL | _custom %= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:280:5 + | +LL | _custom %= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:281:5 + | +LL | _custom *= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:282:5 + | +LL | _custom *= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:285:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:201:10 + --> $DIR/arithmetic_side_effects.rs:286:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:202:10 + --> $DIR/arithmetic_side_effects.rs:287:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:203:10 + --> $DIR/arithmetic_side_effects.rs:288:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:204:10 + --> $DIR/arithmetic_side_effects.rs:289:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:205:10 + --> $DIR/arithmetic_side_effects.rs:290:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:206:10 + --> $DIR/arithmetic_side_effects.rs:291:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:207:10 + --> $DIR/arithmetic_side_effects.rs:292:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:208:10 + --> $DIR/arithmetic_side_effects.rs:293:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:209:10 + --> $DIR/arithmetic_side_effects.rs:294:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:210:10 + --> $DIR/arithmetic_side_effects.rs:295:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:211:10 + --> $DIR/arithmetic_side_effects.rs:296:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:212:10 + --> $DIR/arithmetic_side_effects.rs:297:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:213:10 + --> $DIR/arithmetic_side_effects.rs:298:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:214:10 + --> $DIR/arithmetic_side_effects.rs:299:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:215:10 + --> $DIR/arithmetic_side_effects.rs:300:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:216:10 + --> $DIR/arithmetic_side_effects.rs:301:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:217:10 + --> $DIR/arithmetic_side_effects.rs:302:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:218:10 + --> $DIR/arithmetic_side_effects.rs:303:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:221:13 - | -LL | let _ = Custom + 0; - | ^^^^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:222:13 + --> $DIR/arithmetic_side_effects.rs:304:15 | -LL | let _ = Custom + 1; - | ^^^^^^^^^^ +LL | _custom = _custom + _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:223:13 + --> $DIR/arithmetic_side_effects.rs:305:15 | -LL | let _ = Custom + 2; - | ^^^^^^^^^^ +LL | _custom = _custom + &_custom; + | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:224:13 + --> $DIR/arithmetic_side_effects.rs:306:15 | -LL | let _ = Custom + 0.0; - | ^^^^^^^^^^^^ +LL | _custom = Custom + _custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:225:13 + --> $DIR/arithmetic_side_effects.rs:307:15 | -LL | let _ = Custom + 1.0; - | ^^^^^^^^^^^^ +LL | _custom = &Custom + _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:226:13 + --> $DIR/arithmetic_side_effects.rs:308:15 | -LL | let _ = Custom + 2.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom - Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:227:13 + --> $DIR/arithmetic_side_effects.rs:309:15 | -LL | let _ = Custom - 0; - | ^^^^^^^^^^ +LL | _custom = _custom - &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:228:13 + --> $DIR/arithmetic_side_effects.rs:310:15 | -LL | let _ = Custom - 1; - | ^^^^^^^^^^ +LL | _custom = Custom - _custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:229:13 + --> $DIR/arithmetic_side_effects.rs:311:15 | -LL | let _ = Custom - 2; - | ^^^^^^^^^^ +LL | _custom = &Custom - _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:230:13 + --> $DIR/arithmetic_side_effects.rs:312:15 | -LL | let _ = Custom - 0.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom / Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:231:13 + --> $DIR/arithmetic_side_effects.rs:313:15 | -LL | let _ = Custom - 1.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom / &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:232:13 + --> $DIR/arithmetic_side_effects.rs:314:15 | -LL | let _ = Custom - 2.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom % Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:233:13 + --> $DIR/arithmetic_side_effects.rs:315:15 | -LL | let _ = Custom / 0; - | ^^^^^^^^^^ +LL | _custom = _custom % &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:234:13 + --> $DIR/arithmetic_side_effects.rs:316:15 | -LL | let _ = Custom / 1; - | ^^^^^^^^^^ +LL | _custom = _custom * Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:235:13 + --> $DIR/arithmetic_side_effects.rs:317:15 | -LL | let _ = Custom / 2; - | ^^^^^^^^^^ +LL | _custom = _custom * &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:236:13 + --> $DIR/arithmetic_side_effects.rs:318:15 | -LL | let _ = Custom / 0.0; - | ^^^^^^^^^^^^ +LL | _custom = Custom * _custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:237:13 + --> $DIR/arithmetic_side_effects.rs:319:15 | -LL | let _ = Custom / 1.0; - | ^^^^^^^^^^^^ +LL | _custom = &Custom * _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:238:13 + --> $DIR/arithmetic_side_effects.rs:320:15 | -LL | let _ = Custom / 2.0; - | ^^^^^^^^^^^^ +LL | _custom = Custom + &Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:239:13 + --> $DIR/arithmetic_side_effects.rs:321:15 | -LL | let _ = Custom * 0; - | ^^^^^^^^^^ +LL | _custom = &Custom + Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:240:13 + --> $DIR/arithmetic_side_effects.rs:322:15 | -LL | let _ = Custom * 1; - | ^^^^^^^^^^ +LL | _custom = &Custom + &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:241:13 + --> $DIR/arithmetic_side_effects.rs:325:10 | -LL | let _ = Custom * 2; - | ^^^^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:242:13 - | -LL | let _ = Custom * 0.0; - | ^^^^^^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:243:13 - | -LL | let _ = Custom * 1.0; - | ^^^^^^^^^^^^ +LL | _n = -_n; + | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:244:13 + --> $DIR/arithmetic_side_effects.rs:326:10 | -LL | let _ = Custom * 2.0; - | ^^^^^^^^^^^^ +LL | _n = -&_n; + | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:247:10 + --> $DIR/arithmetic_side_effects.rs:327:15 | -LL | _n = -_n; - | ^^^ +LL | _custom = -_custom; + | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:248:10 + --> $DIR/arithmetic_side_effects.rs:328:15 | -LL | _n = -&_n; - | ^^^^ +LL | _custom = -&_custom; + | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:257:5 + --> $DIR/arithmetic_side_effects.rs:337:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:258:5 + --> $DIR/arithmetic_side_effects.rs:338:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:260:5 + --> $DIR/arithmetic_side_effects.rs:340:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:261:5 + --> $DIR/arithmetic_side_effects.rs:341:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:262:5 + --> $DIR/arithmetic_side_effects.rs:342:5 | LL | i >> 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:263:5 + --> $DIR/arithmetic_side_effects.rs:343:5 | LL | i << 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:272:5 + --> $DIR/arithmetic_side_effects.rs:352:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:273:5 + --> $DIR/arithmetic_side_effects.rs:353:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:274:5 + --> $DIR/arithmetic_side_effects.rs:354:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:276:5 + --> $DIR/arithmetic_side_effects.rs:356:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:278:5 + --> $DIR/arithmetic_side_effects.rs:358:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:279:5 + --> $DIR/arithmetic_side_effects.rs:359:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:281:5 + --> $DIR/arithmetic_side_effects.rs:361:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:283:5 + --> $DIR/arithmetic_side_effects.rs:363:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:284:5 + --> $DIR/arithmetic_side_effects.rs:364:5 | LL | i %= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:285:5 + --> $DIR/arithmetic_side_effects.rs:365:5 | LL | i <<= 3; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:286:5 + --> $DIR/arithmetic_side_effects.rs:366:5 | LL | i >>= 2; | ^^^^^^^ -error: aborting due to 82 previous errors +error: aborting due to 99 previous errors From ce56cf71d956ea78c319ff510651473ca8dce47c Mon Sep 17 00:00:00 2001 From: Raiki Tamura Date: Sat, 7 Jan 2023 20:44:02 +0900 Subject: [PATCH 428/524] chore --- clippy_lints/src/loops/single_element_loop.rs | 14 +------------- clippy_utils/src/visitors.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 7cbc456244e4..744fd61bd135 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -1,7 +1,7 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet_with_applicability}; -use clippy_utils::visitors::for_each_expr; +use clippy_utils::visitors::contains_break_or_continue; use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::Mutability; @@ -9,18 +9,6 @@ use rustc_errors::Applicability; use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat}; use rustc_lint::LateContext; use rustc_span::edition::Edition; -use std::ops::ControlFlow; - -fn contains_break_or_continue(expr: &Expr<'_>) -> bool { - for_each_expr(expr, |e| { - if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some() -} pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 863fb60fcfca..14c01a60b4c3 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -724,3 +724,14 @@ pub fn for_each_local_assignment<'tcx, B>( ControlFlow::Continue(()) } } + +pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool { + for_each_expr(expr, |e| { + if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} From d8877bbd8a04e2fb5edd22f092f1ef360ee8075c Mon Sep 17 00:00:00 2001 From: chansuke Date: Sat, 7 Jan 2023 22:57:30 +0900 Subject: [PATCH 429/524] hotfix: remove `ITER_COUNT` since it is not called --- clippy_utils/src/paths.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 9ca50105ae57..95eebab75677 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -47,7 +47,6 @@ pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; #[cfg(feature = "internal")] pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; -pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] From d23dce54ecbc5b94837ddb77db4b3a5edcdf0cd9 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 8 Jan 2023 20:25:42 +0100 Subject: [PATCH 430/524] add a test against #100898 --- tests/ui/box_default.fixed | 10 ++++++++++ tests/ui/box_default.rs | 10 ++++++++++ tests/ui/box_default.stderr | 8 +++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed index 68ab996a7049..7e9f074fdcab 100644 --- a/tests/ui/box_default.fixed +++ b/tests/ui/box_default.fixed @@ -54,4 +54,14 @@ impl Read for ImplementsDefault { fn issue_9621_dyn_trait() { let _: Box = Box::::default(); + issue_10089(); +} + +fn issue_10089() { + let _closure = || { + #[derive(Default)] + struct WeirdPathed; + + let _ = Box::::default(); + }; } diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index 20019c2ee5a0..5c8d0b8354cc 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -54,4 +54,14 @@ impl Read for ImplementsDefault { fn issue_9621_dyn_trait() { let _: Box = Box::new(ImplementsDefault::default()); + issue_10089(); +} + +fn issue_10089() { + let _closure = || { + #[derive(Default)] + struct WeirdPathed; + + let _ = Box::new(WeirdPathed::default()); + }; } diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index f77c97cdfa26..249eb340f96c 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -84,5 +84,11 @@ error: `Box::new(_)` of default value LL | let _: Box = Box::new(ImplementsDefault::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` -error: aborting due to 14 previous errors +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:65:17 + | +LL | let _ = Box::new(WeirdPathed::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: aborting due to 15 previous errors From 1a7ef02dcb65ee4aa9e7679ad0ce7e8a42bee6bf Mon Sep 17 00:00:00 2001 From: koka Date: Mon, 9 Jan 2023 18:49:46 +0900 Subject: [PATCH 431/524] Fix fp in unnecessary_safety_comment --- clippy_lints/src/undocumented_unsafe_blocks.rs | 12 ++++++++++++ tests/ui/unnecessary_safety_comment.rs | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 2e1b6d8d4ea7..2920684ade33 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -263,6 +263,18 @@ fn expr_has_unnecessary_safety_comment<'tcx>( expr: &'tcx hir::Expr<'tcx>, comment_pos: BytePos, ) -> Option { + if cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, ref node)| { + matches!( + node, + Node::Block(&Block { + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }), + ) + }) { + return None; + } + // this should roughly be the reverse of `block_parents_have_safety_comment` if for_each_expr_with_closures(cx, expr, |expr| match expr.kind { hir::ExprKind::Block( diff --git a/tests/ui/unnecessary_safety_comment.rs b/tests/ui/unnecessary_safety_comment.rs index 7fefea7051d6..89fedb145f88 100644 --- a/tests/ui/unnecessary_safety_comment.rs +++ b/tests/ui/unnecessary_safety_comment.rs @@ -48,4 +48,21 @@ fn unnecessary_on_stmt_and_expr() -> u32 { 24 } +mod issue_10084 { + unsafe fn bar() -> i32 { + 42 + } + + macro_rules! foo { + () => { + // SAFETY: This is necessary + unsafe { bar() } + }; + } + + fn main() { + foo!(); + } +} + fn main() {} From 53c12e085ab9595dff369f26046038afa20e8e5c Mon Sep 17 00:00:00 2001 From: Robert Bastian Date: Mon, 9 Jan 2023 21:29:42 +0100 Subject: [PATCH 432/524] hash xor peq --- clippy_lints/src/derive.rs | 10 ++-------- tests/ui/derive_hash_xor_eq.rs | 19 ------------------ tests/ui/derive_hash_xor_eq.stderr | 32 +----------------------------- 3 files changed, 3 insertions(+), 58 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index cf3483d4c00b..e39a31a8c725 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -243,7 +243,7 @@ fn check_hash_peq<'tcx>( cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); - if peq_is_automatically_derived == hash_is_automatically_derived { + if !hash_is_automatically_derived || peq_is_automatically_derived { return; } @@ -252,17 +252,11 @@ fn check_hash_peq<'tcx>( // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] if trait_ref.substs.type_at(1) == ty { - let mess = if peq_is_automatically_derived { - "you are implementing `Hash` explicitly but have derived `PartialEq`" - } else { - "you are deriving `Hash` but have implemented `PartialEq` explicitly" - }; - span_lint_and_then( cx, DERIVE_HASH_XOR_EQ, span, - mess, + "you are deriving `Hash` but have implemented `PartialEq` explicitly", |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); diff --git a/tests/ui/derive_hash_xor_eq.rs b/tests/ui/derive_hash_xor_eq.rs index 813ddc566464..0804e3cffa1a 100644 --- a/tests/ui/derive_hash_xor_eq.rs +++ b/tests/ui/derive_hash_xor_eq.rs @@ -34,23 +34,4 @@ impl std::hash::Hash for Bah { fn hash(&self, _: &mut H) {} } -#[derive(PartialEq)] -struct Foo2; - -trait Hash {} - -// We don't want to lint on user-defined traits called `Hash` -impl Hash for Foo2 {} - -mod use_hash { - use std::hash::{Hash, Hasher}; - - #[derive(PartialEq)] - struct Foo3; - - impl Hash for Foo3 { - fn hash(&self, _: &mut H) {} - } -} - fn main() {} diff --git a/tests/ui/derive_hash_xor_eq.stderr b/tests/ui/derive_hash_xor_eq.stderr index 16c92397804e..16965aa42d8c 100644 --- a/tests/ui/derive_hash_xor_eq.stderr +++ b/tests/ui/derive_hash_xor_eq.stderr @@ -25,35 +25,5 @@ LL | impl PartialEq for Baz { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) -error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:33:1 - | -LL | / impl std::hash::Hash for Bah { -LL | | fn hash(&self, _: &mut H) {} -LL | | } - | |_^ - | -note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:30:10 - | -LL | #[derive(PartialEq)] - | ^^^^^^^^^ - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:51:5 - | -LL | / impl Hash for Foo3 { -LL | | fn hash(&self, _: &mut H) {} -LL | | } - | |_____^ - | -note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:48:14 - | -LL | #[derive(PartialEq)] - | ^^^^^^^^^ - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors From 8ca900b01ce5ff90511fd9d02c29e50a89fcccf0 Mon Sep 17 00:00:00 2001 From: Robert Bastian Date: Tue, 10 Jan 2023 11:12:54 +0100 Subject: [PATCH 433/524] rename --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/derive.rs | 8 ++++---- clippy_lints/src/renamed_lints.rs | 1 + ...rive_hash_xor_eq.rs => derived_hash_with_manual_eq.rs} | 0 ...h_xor_eq.stderr => derived_hash_with_manual_eq.stderr} | 0 tests/ui/rename.rs | 2 ++ 7 files changed, 9 insertions(+), 5 deletions(-) rename tests/ui/{derive_hash_xor_eq.rs => derived_hash_with_manual_eq.rs} (100%) rename tests/ui/{derive_hash_xor_eq.stderr => derived_hash_with_manual_eq.stderr} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02f3188f8be0..8e31e8f0d981 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4137,6 +4137,7 @@ Released 2018-09-13 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq +[`derived_hash_with_manual_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derived_hash_with_manual_eq [`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2982460c9cfa..91ca73633f06 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -111,7 +111,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::dereference::NEEDLESS_BORROW_INFO, crate::dereference::REF_BINDING_TO_REFERENCE_INFO, crate::derivable_impls::DERIVABLE_IMPLS_INFO, - crate::derive::DERIVE_HASH_XOR_EQ_INFO, + crate::derive::DERIVED_HASH_WITH_MANUAL_EQ_INFO, crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO, crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO, crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO, diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index e39a31a8c725..983a517bc123 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "pre 1.29.0"] - pub DERIVE_HASH_XOR_EQ, + pub DERIVED_HASH_WITH_MANUAL_EQ, correctness, "deriving `Hash` but implementing `PartialEq` explicitly" } @@ -197,7 +197,7 @@ declare_clippy_lint! { declare_lint_pass!(Derive => [ EXPL_IMPL_CLONE_ON_COPY, - DERIVE_HASH_XOR_EQ, + DERIVED_HASH_WITH_MANUAL_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, UNSAFE_DERIVE_DESERIALIZE, DERIVE_PARTIAL_EQ_WITHOUT_EQ @@ -226,7 +226,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { } } -/// Implementation of the `DERIVE_HASH_XOR_EQ` lint. +/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. fn check_hash_peq<'tcx>( cx: &LateContext<'tcx>, span: Span, @@ -254,7 +254,7 @@ fn check_hash_peq<'tcx>( if trait_ref.substs.type_at(1) == ty { span_lint_and_then( cx, - DERIVE_HASH_XOR_EQ, + DERIVED_HASH_WITH_MANUAL_EQ, span, "you are deriving `Hash` but have implemented `PartialEq` explicitly", |diag| { diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 72c25592609b..9f487dedb8cb 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -9,6 +9,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::box_vec", "clippy::box_collection"), ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"), ("clippy::disallowed_method", "clippy::disallowed_methods"), ("clippy::disallowed_type", "clippy::disallowed_types"), ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), diff --git a/tests/ui/derive_hash_xor_eq.rs b/tests/ui/derived_hash_with_manual_eq.rs similarity index 100% rename from tests/ui/derive_hash_xor_eq.rs rename to tests/ui/derived_hash_with_manual_eq.rs diff --git a/tests/ui/derive_hash_xor_eq.stderr b/tests/ui/derived_hash_with_manual_eq.stderr similarity index 100% rename from tests/ui/derive_hash_xor_eq.stderr rename to tests/ui/derived_hash_with_manual_eq.stderr diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 699c0ff464e9..64bc1ca7116c 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -10,6 +10,7 @@ #![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] +#![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] @@ -45,6 +46,7 @@ #![warn(clippy::box_vec)] #![warn(clippy::const_static_lifetime)] #![warn(clippy::cyclomatic_complexity)] +#![warn(clippy::derive_hash_xor_eq)] #![warn(clippy::disallowed_method)] #![warn(clippy::disallowed_type)] #![warn(clippy::eval_order_dependence)] From 96e306843eb7236c67b41c7647d4cd56c43e153a Mon Sep 17 00:00:00 2001 From: dswij Date: Wed, 11 Jan 2023 06:51:52 +0800 Subject: [PATCH 434/524] tests: add case for multiple semis --- tests/ui/needless_return.fixed | 10 +++ tests/ui/needless_return.rs | 10 +++ tests/ui/needless_return.stderr | 110 ++++++++++++++++++-------------- 3 files changed, 83 insertions(+), 47 deletions(-) diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index ab1c0e590bbc..079e3531def1 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -31,6 +31,16 @@ fn test_no_semicolon() -> bool { true } +#[rustfmt::skip] +fn test_multiple_semicolon() -> bool { + true +} + +#[rustfmt::skip] +fn test_multiple_semicolon_with_spaces() -> bool { + true +} + fn test_if_block() -> bool { if true { true diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index abed338bb9b2..c1c48284f086 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -31,6 +31,16 @@ fn test_no_semicolon() -> bool { return true; } +#[rustfmt::skip] +fn test_multiple_semicolon() -> bool { + return true;;; +} + +#[rustfmt::skip] +fn test_multiple_semicolon_with_spaces() -> bool { + return true;; ; ; +} + fn test_if_block() -> bool { if true { return true; diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 52eabf6e1370..08b04bfe9d8b 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -16,7 +16,23 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:36:9 + --> $DIR/needless_return.rs:36:5 + | +LL | return true;;; + | ^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:41:5 + | +LL | return true;; ; ; + | ^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:46:9 | LL | return true; | ^^^^^^^^^^^ @@ -24,7 +40,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:38:9 + --> $DIR/needless_return.rs:48:9 | LL | return false; | ^^^^^^^^^^^^ @@ -32,7 +48,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:44:17 + --> $DIR/needless_return.rs:54:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -40,7 +56,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:46:13 + --> $DIR/needless_return.rs:56:13 | LL | return true; | ^^^^^^^^^^^ @@ -48,7 +64,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:53:9 + --> $DIR/needless_return.rs:63:9 | LL | return true; | ^^^^^^^^^^^ @@ -56,7 +72,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:55:16 + --> $DIR/needless_return.rs:65:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -64,7 +80,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:59:5 + --> $DIR/needless_return.rs:69:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +88,7 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:62:21 + --> $DIR/needless_return.rs:72:21 | LL | fn test_void_fun() { | _____________________^ @@ -82,7 +98,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:67:11 + --> $DIR/needless_return.rs:77:11 | LL | if b { | ___________^ @@ -92,7 +108,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:69:13 + --> $DIR/needless_return.rs:79:13 | LL | } else { | _____________^ @@ -102,7 +118,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:77:14 + --> $DIR/needless_return.rs:87:14 | LL | _ => return, | ^^^^^^ @@ -110,7 +126,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:85:24 + --> $DIR/needless_return.rs:95:24 | LL | let _ = 42; | ________________________^ @@ -120,7 +136,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:88:14 + --> $DIR/needless_return.rs:98:14 | LL | _ => return, | ^^^^^^ @@ -128,7 +144,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:101:9 + --> $DIR/needless_return.rs:111:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +152,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:103:9 + --> $DIR/needless_return.rs:113:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +160,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:125:32 + --> $DIR/needless_return.rs:135:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ @@ -152,7 +168,7 @@ LL | bar.unwrap_or_else(|_| return) = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:129:21 + --> $DIR/needless_return.rs:139:21 | LL | let _ = || { | _____________________^ @@ -162,7 +178,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:132:20 + --> $DIR/needless_return.rs:142:20 | LL | let _ = || return; | ^^^^^^ @@ -170,7 +186,7 @@ LL | let _ = || return; = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:138:32 + --> $DIR/needless_return.rs:148:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ @@ -178,7 +194,7 @@ LL | res.unwrap_or_else(|_| return Foo) = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:147:5 + --> $DIR/needless_return.rs:157:5 | LL | return true; | ^^^^^^^^^^^ @@ -186,7 +202,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:151:5 + --> $DIR/needless_return.rs:161:5 | LL | return true; | ^^^^^^^^^^^ @@ -194,7 +210,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:156:9 + --> $DIR/needless_return.rs:166:9 | LL | return true; | ^^^^^^^^^^^ @@ -202,7 +218,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:158:9 + --> $DIR/needless_return.rs:168:9 | LL | return false; | ^^^^^^^^^^^^ @@ -210,7 +226,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:164:17 + --> $DIR/needless_return.rs:174:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -218,7 +234,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:166:13 + --> $DIR/needless_return.rs:176:13 | LL | return true; | ^^^^^^^^^^^ @@ -226,7 +242,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:173:9 + --> $DIR/needless_return.rs:183:9 | LL | return true; | ^^^^^^^^^^^ @@ -234,7 +250,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:175:16 + --> $DIR/needless_return.rs:185:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -242,7 +258,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:179:5 + --> $DIR/needless_return.rs:189:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -250,7 +266,7 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:182:33 + --> $DIR/needless_return.rs:192:33 | LL | async fn async_test_void_fun() { | _________________________________^ @@ -260,7 +276,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:187:11 + --> $DIR/needless_return.rs:197:11 | LL | if b { | ___________^ @@ -270,7 +286,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:189:13 + --> $DIR/needless_return.rs:199:13 | LL | } else { | _____________^ @@ -280,7 +296,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:197:14 + --> $DIR/needless_return.rs:207:14 | LL | _ => return, | ^^^^^^ @@ -288,7 +304,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:210:9 + --> $DIR/needless_return.rs:220:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -296,7 +312,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:212:9 + --> $DIR/needless_return.rs:222:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -304,7 +320,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:228:5 + --> $DIR/needless_return.rs:238:5 | LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -312,7 +328,7 @@ LL | return format!("Hello {}", "world!"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:239:9 + --> $DIR/needless_return.rs:249:9 | LL | return true; | ^^^^^^^^^^^ @@ -320,7 +336,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:241:9 + --> $DIR/needless_return.rs:251:9 | LL | return false; | ^^^^^^^^^^^^ @@ -328,7 +344,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:248:13 + --> $DIR/needless_return.rs:258:13 | LL | return 10; | ^^^^^^^^^ @@ -336,7 +352,7 @@ LL | return 10; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:251:13 + --> $DIR/needless_return.rs:261:13 | LL | return 100; | ^^^^^^^^^^ @@ -344,7 +360,7 @@ LL | return 100; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:259:9 + --> $DIR/needless_return.rs:269:9 | LL | return 0; | ^^^^^^^^ @@ -352,7 +368,7 @@ LL | return 0; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:266:13 + --> $DIR/needless_return.rs:276:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -360,7 +376,7 @@ LL | return *(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:268:13 + --> $DIR/needless_return.rs:278:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -368,7 +384,7 @@ LL | return !*(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:275:20 + --> $DIR/needless_return.rs:285:20 | LL | let _ = 42; | ____________________^ @@ -379,7 +395,7 @@ LL | | return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:282:20 + --> $DIR/needless_return.rs:292:20 | LL | let _ = 42; return; | ^^^^^^^ @@ -387,7 +403,7 @@ LL | let _ = 42; return; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:294:9 + --> $DIR/needless_return.rs:304:9 | LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -395,12 +411,12 @@ LL | return Ok(format!("ok!")); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:296:9 + --> $DIR/needless_return.rs:306:9 | LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: remove `return` -error: aborting due to 48 previous errors +error: aborting due to 50 previous errors From d73adea465726944815eb62d373abf6e87dc3b12 Mon Sep 17 00:00:00 2001 From: dswij Date: Wed, 11 Jan 2023 06:52:22 +0800 Subject: [PATCH 435/524] `needless_return`: remove multiple semis on suggestion --- clippy_lints/src/returns.rs | 26 ++++++++++++++------------ clippy_utils/src/lib.rs | 4 ++++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index bbbd9e4989e9..42c51d7d0710 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::visitors::{for_each_expr, Descend}; -use clippy_utils::{fn_def_id, path_to_local_id}; +use clippy_utils::{fn_def_id, path_to_local_id, span_find_starting_semi}; use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { kind: FnKind<'tcx>, _: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, - _: Span, + sp: Span, _: HirId, ) { match kind { @@ -166,14 +166,14 @@ impl<'tcx> LateLintPass<'tcx> for Return { check_final_expr(cx, body.value, vec![], replacement); }, FnKind::ItemFn(..) | FnKind::Method(..) => { - check_block_return(cx, &body.value.kind, vec![]); + check_block_return(cx, &body.value.kind, sp, vec![]); }, } } } // if `expr` is a block, check if there are needless returns in it -fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, semi_spans: Vec) { +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec) { if let ExprKind::Block(block, _) = expr_kind { if let Some(block_expr) = block.expr { check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty); @@ -183,12 +183,14 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, check_final_expr(cx, expr, semi_spans, RetReplacement::Empty); }, StmtKind::Semi(semi_expr) => { - let mut semi_spans_and_this_one = semi_spans; - // we only want the span containing the semicolon so we can remove it later. From `entry.rs:382` - if let Some(semicolon_span) = stmt.span.trim_start(semi_expr.span) { - semi_spans_and_this_one.push(semicolon_span); - check_final_expr(cx, semi_expr, semi_spans_and_this_one, RetReplacement::Empty); + // Remove ending semicolons and any whitespace ' ' in between. + // Without `return`, the suggestion might not compile if the semicolon is retained + if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) { + let semi_span_to_remove = + span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi())); + semi_spans.push(semi_span_to_remove); } + check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty); }, _ => (), } @@ -231,9 +233,9 @@ fn check_final_expr<'tcx>( emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement); }, ExprKind::If(_, then, else_clause_opt) => { - check_block_return(cx, &then.kind, semi_spans.clone()); + check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); if let Some(else_clause) = else_clause_opt { - check_block_return(cx, &else_clause.kind, semi_spans); + check_block_return(cx, &else_clause.kind, peeled_drop_expr.span, semi_spans); } }, // a match expr, check all arms @@ -246,7 +248,7 @@ fn check_final_expr<'tcx>( } }, // if it's a whole block, check it - other_expr_kind => check_block_return(cx, other_expr_kind, semi_spans), + other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans), } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1dc68be31d92..76f406abf1c7 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2488,6 +2488,10 @@ pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { comments_buf.join("\n") } +pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { + sm.span_take_while(span, |&ch| ch == ' ' || ch == ';') +} + macro_rules! op_utils { ($($name:ident $assign:ident)*) => { /// Binary operation traits like `LangItem::Add` From 5f8686ec3b598ca33b64c6e1cd31f72214b49e96 Mon Sep 17 00:00:00 2001 From: Albert Larsan <74931857+albertlarsan68@users.noreply.github.com> Date: Thu, 5 Jan 2023 09:45:44 +0100 Subject: [PATCH 436/524] Change `src/test` to `tests` in source files, fix tidy and tests --- tests/ui/crashes/ice-6254.rs | 2 +- tests/ui/crashes/ice-6255.rs | 2 +- tests/ui/crashes/ice-6256.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs index a2a60a169153..8af60890390e 100644 --- a/tests/ui/crashes/ice-6254.rs +++ b/tests/ui/crashes/ice-6254.rs @@ -1,4 +1,4 @@ -// originally from ./src/test/ui/pattern/usefulness/consts-opaque.rs +// originally from ./tests/ui/pattern/usefulness/consts-opaque.rs // panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', // compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5 diff --git a/tests/ui/crashes/ice-6255.rs b/tests/ui/crashes/ice-6255.rs index bd4a81d98e2e..ccde6aa2b0f7 100644 --- a/tests/ui/crashes/ice-6255.rs +++ b/tests/ui/crashes/ice-6255.rs @@ -1,4 +1,4 @@ -// originally from rustc ./src/test/ui/macros/issue-78325-inconsistent-resolution.rs +// originally from rustc ./tests/ui/macros/issue-78325-inconsistent-resolution.rs // inconsistent resolution for a macro macro_rules! define_other_core { diff --git a/tests/ui/crashes/ice-6256.rs b/tests/ui/crashes/ice-6256.rs index 67308263dadd..f9ee3e058c11 100644 --- a/tests/ui/crashes/ice-6256.rs +++ b/tests/ui/crashes/ice-6256.rs @@ -1,4 +1,4 @@ -// originally from rustc ./src/test/ui/regions/issue-78262.rs +// originally from rustc ./tests/ui/regions/issue-78262.rs // ICE: to get the signature of a closure, use substs.as_closure().sig() not fn_sig() #![allow(clippy::upper_case_acronyms)] From bd76d9133b62447035741e0f693de9c98893902a Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 8 Jan 2023 16:19:11 +0100 Subject: [PATCH 437/524] trim paths in `suspicious_to_owned` --- clippy_lints/src/methods/suspicious_to_owned.rs | 6 ++++-- tests/ui/suspicious_to_owned.stderr | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index 15c1c618c513..fe88fa41fd91 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, print::with_forced_trimmed_paths}; use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; @@ -24,7 +24,9 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - cx, SUSPICIOUS_TO_OWNED, expr.span, - &format!("this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned"), + &with_forced_trimmed_paths!(format!( + "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" + )), "consider using, depending on intent", format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"), app, diff --git a/tests/ui/suspicious_to_owned.stderr b/tests/ui/suspicious_to_owned.stderr index ae1aec34d82e..dec3f50d6f1b 100644 --- a/tests/ui/suspicious_to_owned.stderr +++ b/tests/ui/suspicious_to_owned.stderr @@ -1,4 +1,4 @@ -error: this `to_owned` call clones the std::borrow::Cow<'_, str> itself and does not cause the std::borrow::Cow<'_, str> contents to become owned +error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned --> $DIR/suspicious_to_owned.rs:16:13 | LL | let _ = cow.to_owned(); @@ -6,19 +6,19 @@ LL | let _ = cow.to_owned(); | = note: `-D clippy::suspicious-to-owned` implied by `-D warnings` -error: this `to_owned` call clones the std::borrow::Cow<'_, [char; 3]> itself and does not cause the std::borrow::Cow<'_, [char; 3]> contents to become owned +error: this `to_owned` call clones the Cow<'_, [char; 3]> itself and does not cause the Cow<'_, [char; 3]> contents to become owned --> $DIR/suspicious_to_owned.rs:26:13 | LL | let _ = cow.to_owned(); | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` -error: this `to_owned` call clones the std::borrow::Cow<'_, std::vec::Vec> itself and does not cause the std::borrow::Cow<'_, std::vec::Vec> contents to become owned +error: this `to_owned` call clones the Cow<'_, Vec> itself and does not cause the Cow<'_, Vec> contents to become owned --> $DIR/suspicious_to_owned.rs:36:13 | LL | let _ = cow.to_owned(); | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` -error: this `to_owned` call clones the std::borrow::Cow<'_, str> itself and does not cause the std::borrow::Cow<'_, str> contents to become owned +error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned --> $DIR/suspicious_to_owned.rs:46:13 | LL | let _ = cow.to_owned(); From 34024adc88df9d95e2b002250d41a00c5a937234 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 11 Jan 2023 17:25:47 +0000 Subject: [PATCH 438/524] expl_impl_clone_on_copy: ignore packed structs with type/const params --- clippy_lints/src/derive.rs | 13 +++++++++++-- tests/ui/derive.rs | 11 +++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index cf3483d4c00b..df32fbd4b33e 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -14,8 +14,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, - Ty, TyCtxt, + self, Binder, BoundConstness, Clause, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, + TraitPredicate, Ty, TyCtxt, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -366,6 +366,15 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { return; } + // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. + // https://github.com/rust-lang/rust-clippy/issues/10188 + if ty_adt.repr().packed() + && ty_subs + .iter() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) + { + return; + } span_lint_and_note( cx, diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index c629c0e53537..843e1df8bc6b 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -85,4 +85,15 @@ impl Clone for GenericRef<'_, T, U> { } } +// https://github.com/rust-lang/rust-clippy/issues/10188 +#[repr(packed)] +#[derive(Copy)] +struct Packed(T); + +impl Clone for Packed { + fn clone(&self) -> Self { + *self + } +} + fn main() {} From c31944031184ebcd4704280db7233e306eba6967 Mon Sep 17 00:00:00 2001 From: asquared31415 <34665709+asquared31415@users.noreply.github.com> Date: Fri, 23 Dec 2022 13:54:14 -0500 Subject: [PATCH 439/524] add checks for the signature of the lang item --- tests/ui/def_id_nocore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/def_id_nocore.rs b/tests/ui/def_id_nocore.rs index a7da8f89aa3d..1af77d1a25b2 100644 --- a/tests/ui/def_id_nocore.rs +++ b/tests/ui/def_id_nocore.rs @@ -15,7 +15,7 @@ pub trait Copy {} pub unsafe trait Freeze {} #[lang = "start"] -fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize { +fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { 0 } From dd168838b9d353229dc6c9dfb25f3caa66a1d5b2 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 11 Jan 2023 21:42:08 +0100 Subject: [PATCH 440/524] Make clippy compile. --- clippy_utils/src/sugg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index e7879bb196e4..2d1044af17e8 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -219,6 +219,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Repeat(..) | ast::ExprKind::Ret(..) | ast::ExprKind::Yeet(..) + | ast::ExprKind::FormatArgs(..) | ast::ExprKind::Struct(..) | ast::ExprKind::Try(..) | ast::ExprKind::TryBlock(..) From 2bcd697e2d89d0f9f80682fdaaf5bbdd5adbb0ba Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 12 Jan 2023 00:25:09 +0100 Subject: [PATCH 441/524] Update clippy for new format_args!() lang items. --- clippy_lints/src/format_args.rs | 6 ++-- clippy_utils/src/macros.rs | 54 +++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 043112bbc959..70a80d40f464 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -7,14 +7,14 @@ use clippy_utils::macros::{ }; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_type_lang_item}; use if_chain::if_chain; use itertools::Itertools; use rustc_errors::{ Applicability, SuggestionStyle::{CompletelyHidden, ShowCode}, }; -use rustc_hir::{Expr, ExprKind, HirId, QPath}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; @@ -237,7 +237,7 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { ); } - if is_type_diagnostic_item(cx, param_ty, sym::Arguments) && !arg.format.is_default_for_trait() { + if is_type_lang_item(cx, param_ty, LangItem::FormatArguments) && !arg.format.is_default_for_trait() { span_lint_and_then( cx, UNUSED_FORMAT_SPECS, diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 77c5f1155423..a8f8da67b517 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -1,6 +1,5 @@ #![allow(clippy::similar_names)] // `expr` and `expn` -use crate::is_path_diagnostic_item; use crate::source::snippet_opt; use crate::visitors::{for_each_expr, Descend}; @@ -8,7 +7,7 @@ use arrayvec::ArrayVec; use itertools::{izip, Either, Itertools}; use rustc_ast::ast::LitKind; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, Node, QPath}; +use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind}; use rustc_lexer::unescape::unescape_literal; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use rustc_lint::LateContext; @@ -439,8 +438,7 @@ impl<'tcx> FormatArgsValues<'tcx> { // ArgumentV1::from_usize() if let ExprKind::Call(callee, [val]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind - && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind - && path.segments.last().unwrap().ident.name == sym::ArgumentV1 + && let TyKind::Path(QPath::LangItem(LangItem::FormatArgument, _, _)) = ty.kind { let val_idx = if val.span.ctxt() == expr.span.ctxt() && let ExprKind::Field(_, field) = val.kind @@ -486,20 +484,6 @@ struct ParamPosition { impl<'tcx> Visitor<'tcx> for ParamPosition { fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) { - fn parse_count(expr: &Expr<'_>) -> Option { - // ::core::fmt::rt::v1::Count::Param(1usize), - if let ExprKind::Call(ctor, [val]) = expr.kind - && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind - && path.segments.last()?.ident.name == sym::Param - && let ExprKind::Lit(lit) = &val.kind - && let LitKind::Int(pos, _) = lit.node - { - Some(pos as usize) - } else { - None - } - } - match field.ident.name { sym::position => { if let ExprKind::Lit(lit) = &field.expr.kind @@ -519,15 +503,41 @@ impl<'tcx> Visitor<'tcx> for ParamPosition { } } +fn parse_count(expr: &Expr<'_>) -> Option { + // <::core::fmt::rt::v1::Count>::Param(1usize), + if let ExprKind::Call(ctor, [val]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(_, path)) = ctor.kind + && path.ident.name == sym::Param + && let ExprKind::Lit(lit) = &val.kind + && let LitKind::Int(pos, _) = lit.node + { + Some(pos as usize) + } else { + None + } +} + /// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)` fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option + 'tcx> { if let ExprKind::AddrOf(.., array) = fmt_arg.kind && let ExprKind::Array(specs) = array.kind { Some(specs.iter().map(|spec| { - let mut position = ParamPosition::default(); - position.visit_expr(spec); - position + if let ExprKind::Call(f, args) = spec.kind + && let ExprKind::Path(QPath::TypeRelative(ty, f)) = f.kind + && let TyKind::Path(QPath::LangItem(LangItem::FormatPlaceholder, _, _)) = ty.kind + && f.ident.name == sym::new + && let [position, _fill, _align, _flags, precision, width] = args + && let ExprKind::Lit(position) = &position.kind + && let LitKind::Int(position, _) = position.node { + ParamPosition { + value: position as usize, + width: parse_count(width), + precision: parse_count(precision), + } + } else { + ParamPosition::default() + } })) } else { None @@ -890,7 +900,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg) if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind - && is_path_diagnostic_item(cx, ty, sym::Arguments) + && let TyKind::Path(QPath::LangItem(LangItem::FormatArguments, _, _)) = ty.kind && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted") { let format_string = FormatString::new(cx, pieces)?; From 51aaba6e8cf50289d55f53ac85aab4c95d601439 Mon Sep 17 00:00:00 2001 From: navh Date: Tue, 6 Dec 2022 19:05:22 +0100 Subject: [PATCH 442/524] `cast_possible_truncation` Suggest TryFrom when truncation possible --- .../src/casts/cast_possible_truncation.rs | 24 ++++- clippy_lints/src/casts/mod.rs | 2 +- tests/ui/cast.stderr | 95 +++++++++++++++++++ tests/ui/cast_size.stderr | 53 +++++++++++ 4 files changed, 170 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index a6376484914b..d9898aeb92c4 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,7 +1,11 @@ use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::expr_or_init; +use clippy_utils::source::snippet; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; +use rustc_ast::ast; +use rustc_attr::IntType; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -139,7 +143,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, ); return; } - format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}") }, (ty::Float(_), true) => { @@ -153,5 +157,19 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => return, }; - span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg); + let snippet = snippet(cx, expr.span, "x"); + let name_of_cast_from = snippet.split(" as").next().unwrap_or("x"); + let suggestion = format!("{cast_to}::try_from({name_of_cast_from})"); + + span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| { + diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ..."); + diag.span_suggestion_with_style( + expr.span, + "... or use `try_from` and handle the error accordingly", + suggestion, + Applicability::Unspecified, + // always show the suggestion in a separate line + SuggestionStyle::ShowAlways, + ); + }); } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 161e3a698e9e..38b1c5c1c7e7 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -85,7 +85,7 @@ declare_clippy_lint! { /// ### Why is this bad? /// In some problem domains, it is good practice to avoid /// truncation. This lint can be activated to help assess where additional - /// checks could be beneficial. + /// checks could be beneficial, and suggests implementing TryFrom trait. /// /// ### Example /// ```rust diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 0c63b4af3086..eceb135d62ba 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -42,13 +42,24 @@ error: casting `f32` to `i32` may truncate the value LL | 1f32 as i32; | ^^^^^^^^^^^ | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` +help: ... or use `try_from` and handle the error accordingly + | +LL | i32::try_from(1f32); + | ~~~~~~~~~~~~~~~~~~~ error: casting `f32` to `u32` may truncate the value --> $DIR/cast.rs:25:5 | LL | 1f32 as u32; | ^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u32::try_from(1f32); + | ~~~~~~~~~~~~~~~~~~~ error: casting `f32` to `u32` may lose the sign of the value --> $DIR/cast.rs:25:5 @@ -63,30 +74,60 @@ error: casting `f64` to `f32` may truncate the value | LL | 1f64 as f32; | ^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | f32::try_from(1f64); + | ~~~~~~~~~~~~~~~~~~~ error: casting `i32` to `i8` may truncate the value --> $DIR/cast.rs:27:5 | LL | 1i32 as i8; | ^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | i8::try_from(1i32); + | ~~~~~~~~~~~~~~~~~~ error: casting `i32` to `u8` may truncate the value --> $DIR/cast.rs:28:5 | LL | 1i32 as u8; | ^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u8::try_from(1i32); + | ~~~~~~~~~~~~~~~~~~ error: casting `f64` to `isize` may truncate the value --> $DIR/cast.rs:29:5 | LL | 1f64 as isize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | isize::try_from(1f64); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `f64` to `usize` may truncate the value --> $DIR/cast.rs:30:5 | LL | 1f64 as usize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | usize::try_from(1f64); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `f64` to `usize` may lose the sign of the value --> $DIR/cast.rs:30:5 @@ -143,18 +184,36 @@ error: casting `i64` to `i8` may truncate the value | LL | (-99999999999i64).min(1) as i8; // should be linted because signed | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | i8::try_from((-99999999999i64).min(1)); // should be linted because signed + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `u8` may truncate the value --> $DIR/cast.rs:120:5 | LL | 999999u64.clamp(0, 256) as u8; // should still be linted | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u8::try_from(999999u64.clamp(0, 256)); // should still be linted + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E2` to `u8` may truncate the value --> $DIR/cast.rs:141:21 | LL | let _ = self as u8; | ^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let _ = u8::try_from(self); + | ~~~~~~~~~~~~~~~~~~ error: casting `main::E2::B` to `u8` will truncate the value --> $DIR/cast.rs:142:21 @@ -169,6 +228,12 @@ error: casting `main::E5` to `i8` may truncate the value | LL | let _ = self as i8; | ^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let _ = i8::try_from(self); + | ~~~~~~~~~~~~~~~~~~ error: casting `main::E5::A` to `i8` will truncate the value --> $DIR/cast.rs:179:21 @@ -181,30 +246,60 @@ error: casting `main::E6` to `i16` may truncate the value | LL | let _ = self as i16; | ^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let _ = i16::try_from(self); + | ~~~~~~~~~~~~~~~~~~~ error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers --> $DIR/cast.rs:208:21 | LL | let _ = self as usize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let _ = usize::try_from(self); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E10` to `u16` may truncate the value --> $DIR/cast.rs:249:21 | LL | let _ = self as u16; | ^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let _ = u16::try_from(self); + | ~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value --> $DIR/cast.rs:257:13 | LL | let c = (q >> 16) as u8; | ^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let c = u8::try_from((q >> 16)); + | ~~~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value --> $DIR/cast.rs:260:13 | LL | let c = (q / 1000) as u8; | ^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let c = u8::try_from((q / 1000)); + | ~~~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 33 previous errors diff --git a/tests/ui/cast_size.stderr b/tests/ui/cast_size.stderr index 95552f2e2853..8acf26049f4d 100644 --- a/tests/ui/cast_size.stderr +++ b/tests/ui/cast_size.stderr @@ -4,7 +4,12 @@ error: casting `isize` to `i8` may truncate the value LL | 1isize as i8; | ^^^^^^^^^^^^ | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` +help: ... or use `try_from` and handle the error accordingly + | +LL | i8::try_from(1isize); + | ~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) --> $DIR/cast_size.rs:15:5 @@ -37,24 +42,48 @@ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wi | LL | 1isize as i32; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | i32::try_from(1isize); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers --> $DIR/cast_size.rs:20:5 | LL | 1isize as u32; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u32::try_from(1isize); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers --> $DIR/cast_size.rs:21:5 | LL | 1usize as u32; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u32::try_from(1usize); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers --> $DIR/cast_size.rs:22:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | i32::try_from(1usize); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers --> $DIR/cast_size.rs:22:5 @@ -69,18 +98,36 @@ error: casting `i64` to `isize` may truncate the value on targets with 32-bit wi | LL | 1i64 as isize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | isize::try_from(1i64); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers --> $DIR/cast_size.rs:25:5 | LL | 1i64 as usize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | usize::try_from(1i64); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers --> $DIR/cast_size.rs:26:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | isize::try_from(1u64); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers --> $DIR/cast_size.rs:26:5 @@ -93,6 +140,12 @@ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wi | LL | 1u64 as usize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | usize::try_from(1u64); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers --> $DIR/cast_size.rs:28:5 From fcdd08badf90665832939ccd30dc6df3194dd8a4 Mon Sep 17 00:00:00 2001 From: navh Date: Tue, 6 Dec 2022 19:05:49 +0100 Subject: [PATCH 443/524] update `cast_possible_truncation` documentation --- clippy_lints/src/casts/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 38b1c5c1c7e7..a9618ca06d64 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -80,12 +80,13 @@ declare_clippy_lint! { /// ### What it does /// Checks for casts between numerical types that may /// truncate large values. This is expected behavior, so the cast is `Allow` by - /// default. + /// default. It suggests user either explicitly ignore the lint, + /// or use `try_from()` and handle the truncation, default, or panic explicitly. /// /// ### Why is this bad? /// In some problem domains, it is good practice to avoid /// truncation. This lint can be activated to help assess where additional - /// checks could be beneficial, and suggests implementing TryFrom trait. + /// checks could be beneficial. /// /// ### Example /// ```rust From e6948c4117b608585c5aa22a7f95b6cfdc88dc48 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 6 Dec 2022 19:07:18 +0100 Subject: [PATCH 444/524] Last PR adjustments --- .../src/casts/cast_possible_truncation.rs | 6 ++---- clippy_lints/src/casts/mod.rs | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index d9898aeb92c4..7a6450ffaa5e 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -3,8 +3,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::expr_or_init; use clippy_utils::source::snippet; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; -use rustc_ast::ast; -use rustc_attr::IntType; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -157,8 +155,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => return, }; - let snippet = snippet(cx, expr.span, "x"); - let name_of_cast_from = snippet.split(" as").next().unwrap_or("x"); + let snippet = snippet(cx, expr.span, ".."); + let name_of_cast_from = snippet.split(" as").next().unwrap_or(".."); let suggestion = format!("{cast_to}::try_from({name_of_cast_from})"); span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| { diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index a9618ca06d64..64dbe6c224c7 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -94,6 +94,21 @@ declare_clippy_lint! { /// x as u8 /// } /// ``` + /// Use instead: + /// ``` + /// fn as_u8(x: u64) -> u8 { + /// if let Ok(x) = u8::try_from(x) { + /// x + /// } else { + /// todo!(); + /// } + /// } + /// // Or + /// #[allow(clippy::cast_possible_truncation)] + /// fn as_u16(x: u64) -> u16 { + /// x as u16 + /// } + /// ``` #[clippy::version = "pre 1.29.0"] pub CAST_POSSIBLE_TRUNCATION, pedantic, From 5cb6246c3e7ccae640eafbc238a293918b552116 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 12 Jan 2023 12:35:29 +0100 Subject: [PATCH 445/524] Address PR reivew --- .../src/casts/cast_possible_truncation.rs | 16 +++-- clippy_lints/src/casts/mod.rs | 2 +- tests/ui/cast.rs | 1 + tests/ui/cast.stderr | 68 +++++++++++++------ 4 files changed, 63 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 7a6450ffaa5e..f3f8b8d87982 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -8,6 +8,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_span::Span; use rustc_target::abi::IntegerType; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; @@ -76,7 +77,14 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b } } -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + cast_to_span: Span, +) { let msg = match (cast_from.kind(), cast_to.is_integral()) { (ty::Int(_) | ty::Uint(_), true) => { let from_nbits = apply_reductions( @@ -155,9 +163,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => return, }; - let snippet = snippet(cx, expr.span, ".."); - let name_of_cast_from = snippet.split(" as").next().unwrap_or(".."); - let suggestion = format!("{cast_to}::try_from({name_of_cast_from})"); + let name_of_cast_from = snippet(cx, cast_expr.span, ".."); + let cast_to_snip = snippet(cx, cast_to_span, ".."); + let suggestion = format!("{cast_to_snip}::try_from({name_of_cast_from})"); span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| { diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ..."); diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 64dbe6c224c7..362f70d12d18 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -728,7 +728,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) { - cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to); + cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index e6031e9adaeb..8b2673c2a7fd 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -28,6 +28,7 @@ fn main() { 1i32 as u8; 1f64 as isize; 1f64 as usize; + 1f32 as u32 as u16; // Test clippy::cast_possible_wrap 1u8 as i8; 1u16 as i16; diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index eceb135d62ba..4af1de9aa38d 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -135,8 +135,38 @@ error: casting `f64` to `usize` may lose the sign of the value LL | 1f64 as usize; | ^^^^^^^^^^^^^ +error: casting `u32` to `u16` may truncate the value + --> $DIR/cast.rs:31:5 + | +LL | 1f32 as u32 as u16; + | ^^^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u16::try_from(1f32 as u32); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: casting `f32` to `u32` may truncate the value + --> $DIR/cast.rs:31:5 + | +LL | 1f32 as u32 as u16; + | ^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u32::try_from(1f32) as u16; + | ~~~~~~~~~~~~~~~~~~~ + +error: casting `f32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:31:5 + | +LL | 1f32 as u32 as u16; + | ^^^^^^^^^^^ + error: casting `u8` to `i8` may wrap around the value - --> $DIR/cast.rs:32:5 + --> $DIR/cast.rs:33:5 | LL | 1u8 as i8; | ^^^^^^^^^ @@ -144,43 +174,43 @@ LL | 1u8 as i8; = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` error: casting `u16` to `i16` may wrap around the value - --> $DIR/cast.rs:33:5 + --> $DIR/cast.rs:34:5 | LL | 1u16 as i16; | ^^^^^^^^^^^ error: casting `u32` to `i32` may wrap around the value - --> $DIR/cast.rs:34:5 + --> $DIR/cast.rs:35:5 | LL | 1u32 as i32; | ^^^^^^^^^^^ error: casting `u64` to `i64` may wrap around the value - --> $DIR/cast.rs:35:5 + --> $DIR/cast.rs:36:5 | LL | 1u64 as i64; | ^^^^^^^^^^^ error: casting `usize` to `isize` may wrap around the value - --> $DIR/cast.rs:36:5 + --> $DIR/cast.rs:37:5 | LL | 1usize as isize; | ^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> $DIR/cast.rs:39:5 + --> $DIR/cast.rs:40:5 | LL | -1i32 as u32; | ^^^^^^^^^^^^ error: casting `isize` to `usize` may lose the sign of the value - --> $DIR/cast.rs:41:5 + --> $DIR/cast.rs:42:5 | LL | -1isize as usize; | ^^^^^^^^^^^^^^^^ error: casting `i64` to `i8` may truncate the value - --> $DIR/cast.rs:108:5 + --> $DIR/cast.rs:109:5 | LL | (-99999999999i64).min(1) as i8; // should be linted because signed | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -192,7 +222,7 @@ LL | i8::try_from((-99999999999i64).min(1)); // should be linted because sig | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `u8` may truncate the value - --> $DIR/cast.rs:120:5 + --> $DIR/cast.rs:121:5 | LL | 999999u64.clamp(0, 256) as u8; // should still be linted | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -204,7 +234,7 @@ LL | u8::try_from(999999u64.clamp(0, 256)); // should still be linted | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E2` to `u8` may truncate the value - --> $DIR/cast.rs:141:21 + --> $DIR/cast.rs:142:21 | LL | let _ = self as u8; | ^^^^^^^^^^ @@ -216,7 +246,7 @@ LL | let _ = u8::try_from(self); | ~~~~~~~~~~~~~~~~~~ error: casting `main::E2::B` to `u8` will truncate the value - --> $DIR/cast.rs:142:21 + --> $DIR/cast.rs:143:21 | LL | let _ = Self::B as u8; | ^^^^^^^^^^^^^ @@ -224,7 +254,7 @@ LL | let _ = Self::B as u8; = note: `-D clippy::cast-enum-truncation` implied by `-D warnings` error: casting `main::E5` to `i8` may truncate the value - --> $DIR/cast.rs:178:21 + --> $DIR/cast.rs:179:21 | LL | let _ = self as i8; | ^^^^^^^^^^ @@ -236,13 +266,13 @@ LL | let _ = i8::try_from(self); | ~~~~~~~~~~~~~~~~~~ error: casting `main::E5::A` to `i8` will truncate the value - --> $DIR/cast.rs:179:21 + --> $DIR/cast.rs:180:21 | LL | let _ = Self::A as i8; | ^^^^^^^^^^^^^ error: casting `main::E6` to `i16` may truncate the value - --> $DIR/cast.rs:193:21 + --> $DIR/cast.rs:194:21 | LL | let _ = self as i16; | ^^^^^^^^^^^ @@ -254,7 +284,7 @@ LL | let _ = i16::try_from(self); | ~~~~~~~~~~~~~~~~~~~ error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast.rs:208:21 + --> $DIR/cast.rs:209:21 | LL | let _ = self as usize; | ^^^^^^^^^^^^^ @@ -266,7 +296,7 @@ LL | let _ = usize::try_from(self); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E10` to `u16` may truncate the value - --> $DIR/cast.rs:249:21 + --> $DIR/cast.rs:250:21 | LL | let _ = self as u16; | ^^^^^^^^^^^ @@ -278,7 +308,7 @@ LL | let _ = u16::try_from(self); | ~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value - --> $DIR/cast.rs:257:13 + --> $DIR/cast.rs:258:13 | LL | let c = (q >> 16) as u8; | ^^^^^^^^^^^^^^^ @@ -290,7 +320,7 @@ LL | let c = u8::try_from((q >> 16)); | ~~~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value - --> $DIR/cast.rs:260:13 + --> $DIR/cast.rs:261:13 | LL | let c = (q / 1000) as u8; | ^^^^^^^^^^^^^^^^ @@ -301,5 +331,5 @@ help: ... or use `try_from` and handle the error accordingly LL | let c = u8::try_from((q / 1000)); | ~~~~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 33 previous errors +error: aborting due to 36 previous errors From 920633258f237e3aed59adb1e9e900431962ab7a Mon Sep 17 00:00:00 2001 From: Robert Bastian Date: Thu, 12 Jan 2023 14:22:18 +0100 Subject: [PATCH 446/524] fix --- tests/ui/derived_hash_with_manual_eq.rs | 2 + tests/ui/derived_hash_with_manual_eq.stderr | 10 +-- tests/ui/rename.fixed | 2 + tests/ui/rename.stderr | 90 +++++++++++---------- 4 files changed, 57 insertions(+), 47 deletions(-) diff --git a/tests/ui/derived_hash_with_manual_eq.rs b/tests/ui/derived_hash_with_manual_eq.rs index 0804e3cffa1a..8ad09a8de43d 100644 --- a/tests/ui/derived_hash_with_manual_eq.rs +++ b/tests/ui/derived_hash_with_manual_eq.rs @@ -27,6 +27,8 @@ impl PartialEq for Baz { } } +// Implementing `Hash` with a derived `PartialEq` is fine. See #2627 + #[derive(PartialEq)] struct Bah; diff --git a/tests/ui/derived_hash_with_manual_eq.stderr b/tests/ui/derived_hash_with_manual_eq.stderr index 16965aa42d8c..230940f25fb6 100644 --- a/tests/ui/derived_hash_with_manual_eq.stderr +++ b/tests/ui/derived_hash_with_manual_eq.stderr @@ -1,25 +1,25 @@ error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:12:10 + --> $DIR/derived_hash_with_manual_eq.rs:12:10 | LL | #[derive(Hash)] | ^^^^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:15:1 + --> $DIR/derived_hash_with_manual_eq.rs:15:1 | LL | impl PartialEq for Bar { | ^^^^^^^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default + = note: `#[deny(clippy::derived_hash_with_manual_eq)]` on by default = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:21:10 + --> $DIR/derived_hash_with_manual_eq.rs:21:10 | LL | #[derive(Hash)] | ^^^^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:24:1 + --> $DIR/derived_hash_with_manual_eq.rs:24:1 | LL | impl PartialEq for Baz { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 2f76b5752960..5076f61334d6 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -10,6 +10,7 @@ #![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] +#![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] @@ -45,6 +46,7 @@ #![warn(clippy::box_collection)] #![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::cognitive_complexity)] +#![warn(clippy::derived_hash_with_manual_eq)] #![warn(clippy::disallowed_methods)] #![warn(clippy::disallowed_types)] #![warn(clippy::mixed_read_write_in_expression)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 9af58dc75a68..27a0263292ef 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:41:9 + --> $DIR/rename.rs:42:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -7,244 +7,250 @@ LL | #![warn(clippy::almost_complete_letter_range)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:42:9 + --> $DIR/rename.rs:43:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:43:9 + --> $DIR/rename.rs:44:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:44:9 + --> $DIR/rename.rs:45:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:45:9 + --> $DIR/rename.rs:46:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:46:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:47:9 + --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` +error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` + --> $DIR/rename.rs:49:9 + | +LL | #![warn(clippy::derive_hash_xor_eq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` + error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:48:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:49:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 41 previous errors +error: aborting due to 42 previous errors From cfe8849a622c6308eaee333e8adba6c5bc3f457e Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Thu, 12 Jan 2023 06:43:17 -0700 Subject: [PATCH 447/524] Document extending list type configs Signed-off-by: Tyler Weaver --- README.md | 9 +++++++++ book/src/configuration.md | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/README.md b/README.md index 81254ba8b8b8..f7e03ca4cd8c 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,15 @@ cognitive-complexity-threshold = 30 See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), the lint descriptions contain the names and meanings of these configuration variables. +For configurations that are a list type with default values such as +[disallowed-names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names), +you can use the unique value `".."` to extend the default values instead of replacing them. + +```toml +# default of disallowed-names is ["foo", "baz", "quux"] +disallowed-names = ["bar", ".."] # -> ["bar", "foo", "baz", "quux"] +``` + > **Note** > > `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints. diff --git a/book/src/configuration.md b/book/src/configuration.md index 430ff8b739ae..bbe1081ccad9 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -14,6 +14,15 @@ cognitive-complexity-threshold = 30 See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), the lint descriptions contain the names and meanings of these configuration variables. +For configurations that are a list type with default values such as +[disallowed-names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names), +you can use the unique value `".."` to extend the default values instead of replacing them. + +```toml +# default of disallowed-names is ["foo", "baz", "quux"] +disallowed-names = ["bar", ".."] # -> ["bar", "foo", "baz", "quux"] +``` + To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. From 0c48b5a223a0cb8ef431eda4e57a172f678ec12d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 5 Dec 2022 17:52:17 +0000 Subject: [PATCH 448/524] Feed the `features_query` instead of grabbing it from the session lazily --- clippy_lints/src/attrs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0710ac0bb0a7..751c262673b1 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -472,7 +472,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) { // Check for the feature - if !cx.tcx.sess.features_untracked().lint_reasons { + if !cx.tcx.features().lint_reasons { return; } From 321c530fadb766c594698be7c83ab7cbc443bf1c Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 3 Jan 2023 18:04:28 +0000 Subject: [PATCH 449/524] Don't pass `--sysroot` twice if SYSROOT is set This is useful for rust-lang/rust to allow setting a sysroot that's *only* for build scripts, different from the regular sysroot passed in RUSTFLAGS (since cargo doesn't apply RUSTFLAGS to build scripts or proc-macros). That said, the exact motivation is not particularly important: this fixes a regression from https://github.com/rust-lang/rust-clippy/pull/9881/commits/5907e9155ed7f1312d108aa2110853472da3b029#r1060215684. Note that only RUSTFLAGS is tested in the new integration test; passing --sysroot through `clippy-driver` never worked as far as I can tell, and no one is using it, so I didn't fix it here. --- src/driver.rs | 5 ++- tests/integration.rs | 97 +++++++++++++++++++++++++++++++++----------- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index bcc096c570e1..d521e8d88398 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -256,11 +256,14 @@ pub fn main() { LazyLock::force(&ICE_HOOK); exit(rustc_driver::catch_with_exit_code(move || { let mut orig_args: Vec = env::args().collect(); + let has_sysroot_arg = arg_value(&orig_args, "--sysroot", |_| true).is_some(); let sys_root_env = std::env::var("SYSROOT").ok(); let pass_sysroot_env_if_given = |args: &mut Vec, sys_root_env| { if let Some(sys_root) = sys_root_env { - args.extend(vec!["--sysroot".into(), sys_root]); + if !has_sysroot_arg { + args.extend(vec!["--sysroot".into(), sys_root]); + } }; }; diff --git a/tests/integration.rs b/tests/integration.rs index 818ff70b33f4..319e8eb2da60 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,19 +1,42 @@ +//! To run this test, use +//! `env INTEGRATION=rust-lang/log cargo test --test integration --features=integration` +//! +//! You can use a different `INTEGRATION` value to test different repositories. + #![cfg(feature = "integration")] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] -use std::env; use std::ffi::OsStr; +use std::path::{Path, PathBuf}; use std::process::Command; +use std::{env, eprintln}; #[cfg(not(windows))] const CARGO_CLIPPY: &str = "cargo-clippy"; #[cfg(windows)] const CARGO_CLIPPY: &str = "cargo-clippy.exe"; -#[cfg_attr(feature = "integration", test)] -fn integration_test() { - let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set"); +// NOTE: arguments passed to the returned command will be `clippy-driver` args, not `cargo-clippy` +// args. Use `cargo_args` to pass arguments to cargo-clippy. +fn clippy_command(repo_dir: &Path, cargo_args: &[&str]) -> Command { + let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let target_dir = option_env!("CARGO_TARGET_DIR").map_or_else(|| root_dir.join("target"), PathBuf::from); + let clippy_binary = target_dir.join(env!("PROFILE")).join(CARGO_CLIPPY); + + let mut cargo_clippy = Command::new(clippy_binary); + cargo_clippy + .current_dir(repo_dir) + .env("RUST_BACKTRACE", "full") + .env("CARGO_TARGET_DIR", root_dir.join("target")) + .args(["clippy", "--all-targets", "--all-features"]) + .args(cargo_args) + .args(["--", "--cap-lints", "warn", "-Wclippy::pedantic", "-Wclippy::nursery"]); + cargo_clippy +} + +/// Return a directory with a checkout of the repository in `INTEGRATION`. +fn repo_dir(repo_name: &str) -> PathBuf { let repo_url = format!("https://github.com/{repo_name}"); let crate_name = repo_name .split('/') @@ -34,28 +57,19 @@ fn integration_test() { .expect("unable to run git"); assert!(st.success()); - let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let target_dir = std::path::Path::new(&root_dir).join("target"); - let clippy_binary = target_dir.join(env!("PROFILE")).join(CARGO_CLIPPY); - - let output = Command::new(clippy_binary) - .current_dir(repo_dir) - .env("RUST_BACKTRACE", "full") - .env("CARGO_TARGET_DIR", target_dir) - .args([ - "clippy", - "--all-targets", - "--all-features", - "--", - "--cap-lints", - "warn", - "-Wclippy::pedantic", - "-Wclippy::nursery", - ]) - .output() - .expect("unable to run clippy"); + repo_dir +} +#[cfg_attr(feature = "integration", test)] +fn integration_test() { + let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set"); + let repo_dir = repo_dir(&repo_name); + let output = clippy_command(&repo_dir, &[]).output().expect("failed to run clippy"); let stderr = String::from_utf8_lossy(&output.stderr); + if !stderr.is_empty() { + eprintln!("{stderr}"); + } + if let Some(backtrace_start) = stderr.find("error: internal compiler error") { static BACKTRACE_END_MSG: &str = "end of query stack"; let backtrace_end = stderr[backtrace_start..] @@ -90,3 +104,38 @@ fn integration_test() { None => panic!("Process terminated by signal"), } } + +#[cfg_attr(feature = "integration", test)] +fn test_sysroot() { + #[track_caller] + fn verify_cmd(cmd: &mut Command) { + // Test that SYSROOT is ignored if `--sysroot` is passed explicitly. + cmd.env("SYSROOT", "/dummy/value/does/not/exist"); + // We don't actually care about emitting lints, we only want to verify clippy doesn't give a hard + // error. + cmd.arg("-Awarnings"); + let output = cmd.output().expect("failed to run clippy"); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!(stderr.is_empty(), "clippy printed an error: {stderr}"); + assert!(output.status.success(), "clippy exited with an error"); + } + + let rustc = std::env::var("RUSTC").unwrap_or("rustc".to_string()); + let rustc_output = Command::new(rustc) + .args(["--print", "sysroot"]) + .output() + .expect("unable to run rustc"); + assert!(rustc_output.status.success()); + let sysroot = String::from_utf8(rustc_output.stdout).unwrap(); + let sysroot = sysroot.trim_end(); + + // This is a fairly small repo; we want to avoid checking out anything heavy twice, so just + // hard-code it. + let repo_name = "rust-lang/log"; + let repo_dir = repo_dir(repo_name); + // Pass the sysroot through RUSTFLAGS. + verify_cmd(clippy_command(&repo_dir, &["--quiet"]).env("RUSTFLAGS", format!("--sysroot={sysroot}"))); + // NOTE: we don't test passing the arguments directly to clippy-driver (with `-- --sysroot`) + // because it breaks for some reason. I (@jyn514) haven't taken time to track down the bug + // because rust-lang/rust uses RUSTFLAGS and nearly no one else uses --sysroot. +} From fe007179ec3560f86bd15686d60372f8307ca225 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 12 Jan 2023 18:30:51 +0100 Subject: [PATCH 450/524] Add cargo-clippy sysroot test Whne SYSROOT is defined, clippy-driver will insert a --sysroot argument when calling rustc. However, when a sysroot argument is already defined, e.g. through RUSTFLAGS=--sysroot=... the `cargo clippy` call would error. This tests that the sysroot argument is only passed once and that SYSROOT is ignored in this case. --- .github/driver.sh | 7 ++++ tests/integration.rs | 98 +++++++++++++------------------------------- 2 files changed, 36 insertions(+), 69 deletions(-) diff --git a/.github/driver.sh b/.github/driver.sh index 6ff189fc8592..798782340ee7 100644 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -17,6 +17,13 @@ test "$sysroot" = $desired_sysroot sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot) test "$sysroot" = $desired_sysroot +# Check that the --sysroot argument is only passed once (SYSROOT is ignored) +( + cd rustc_tools_util + touch src/lib.rs + SYSROOT=/tmp RUSTFLAGS="--sysroot=$(rustc --print sysroot)" ../target/debug/cargo-clippy clippy --verbose +) + # Make sure this isn't set - clippy-driver should cope without it unset CARGO_MANIFEST_DIR diff --git a/tests/integration.rs b/tests/integration.rs index 319e8eb2da60..a771d8b87c81 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,42 +1,28 @@ -//! To run this test, use +//! This test is meant to only be run in CI. To run it locally use: +//! //! `env INTEGRATION=rust-lang/log cargo test --test integration --features=integration` //! //! You can use a different `INTEGRATION` value to test different repositories. +//! +//! This test will clone the specified repository and run Clippy on it. The test succeeds, if +//! Clippy doesn't produce an ICE. Lint warnings are ignored by this test. #![cfg(feature = "integration")] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] +use std::env; use std::ffi::OsStr; -use std::path::{Path, PathBuf}; use std::process::Command; -use std::{env, eprintln}; #[cfg(not(windows))] const CARGO_CLIPPY: &str = "cargo-clippy"; #[cfg(windows)] const CARGO_CLIPPY: &str = "cargo-clippy.exe"; -// NOTE: arguments passed to the returned command will be `clippy-driver` args, not `cargo-clippy` -// args. Use `cargo_args` to pass arguments to cargo-clippy. -fn clippy_command(repo_dir: &Path, cargo_args: &[&str]) -> Command { - let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let target_dir = option_env!("CARGO_TARGET_DIR").map_or_else(|| root_dir.join("target"), PathBuf::from); - let clippy_binary = target_dir.join(env!("PROFILE")).join(CARGO_CLIPPY); - - let mut cargo_clippy = Command::new(clippy_binary); - cargo_clippy - .current_dir(repo_dir) - .env("RUST_BACKTRACE", "full") - .env("CARGO_TARGET_DIR", root_dir.join("target")) - .args(["clippy", "--all-targets", "--all-features"]) - .args(cargo_args) - .args(["--", "--cap-lints", "warn", "-Wclippy::pedantic", "-Wclippy::nursery"]); - cargo_clippy -} - -/// Return a directory with a checkout of the repository in `INTEGRATION`. -fn repo_dir(repo_name: &str) -> PathBuf { +#[cfg_attr(feature = "integration", test)] +fn integration_test() { + let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set"); let repo_url = format!("https://github.com/{repo_name}"); let crate_name = repo_name .split('/') @@ -57,19 +43,28 @@ fn repo_dir(repo_name: &str) -> PathBuf { .expect("unable to run git"); assert!(st.success()); - repo_dir -} + let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let target_dir = std::path::Path::new(&root_dir).join("target"); + let clippy_binary = target_dir.join(env!("PROFILE")).join(CARGO_CLIPPY); -#[cfg_attr(feature = "integration", test)] -fn integration_test() { - let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set"); - let repo_dir = repo_dir(&repo_name); - let output = clippy_command(&repo_dir, &[]).output().expect("failed to run clippy"); - let stderr = String::from_utf8_lossy(&output.stderr); - if !stderr.is_empty() { - eprintln!("{stderr}"); - } + let output = Command::new(clippy_binary) + .current_dir(repo_dir) + .env("RUST_BACKTRACE", "full") + .env("CARGO_TARGET_DIR", target_dir) + .args([ + "clippy", + "--all-targets", + "--all-features", + "--", + "--cap-lints", + "warn", + "-Wclippy::pedantic", + "-Wclippy::nursery", + ]) + .output() + .expect("unable to run clippy"); + let stderr = String::from_utf8_lossy(&output.stderr); if let Some(backtrace_start) = stderr.find("error: internal compiler error") { static BACKTRACE_END_MSG: &str = "end of query stack"; let backtrace_end = stderr[backtrace_start..] @@ -104,38 +99,3 @@ fn integration_test() { None => panic!("Process terminated by signal"), } } - -#[cfg_attr(feature = "integration", test)] -fn test_sysroot() { - #[track_caller] - fn verify_cmd(cmd: &mut Command) { - // Test that SYSROOT is ignored if `--sysroot` is passed explicitly. - cmd.env("SYSROOT", "/dummy/value/does/not/exist"); - // We don't actually care about emitting lints, we only want to verify clippy doesn't give a hard - // error. - cmd.arg("-Awarnings"); - let output = cmd.output().expect("failed to run clippy"); - let stderr = String::from_utf8_lossy(&output.stderr); - assert!(stderr.is_empty(), "clippy printed an error: {stderr}"); - assert!(output.status.success(), "clippy exited with an error"); - } - - let rustc = std::env::var("RUSTC").unwrap_or("rustc".to_string()); - let rustc_output = Command::new(rustc) - .args(["--print", "sysroot"]) - .output() - .expect("unable to run rustc"); - assert!(rustc_output.status.success()); - let sysroot = String::from_utf8(rustc_output.stdout).unwrap(); - let sysroot = sysroot.trim_end(); - - // This is a fairly small repo; we want to avoid checking out anything heavy twice, so just - // hard-code it. - let repo_name = "rust-lang/log"; - let repo_dir = repo_dir(repo_name); - // Pass the sysroot through RUSTFLAGS. - verify_cmd(clippy_command(&repo_dir, &["--quiet"]).env("RUSTFLAGS", format!("--sysroot={sysroot}"))); - // NOTE: we don't test passing the arguments directly to clippy-driver (with `-- --sysroot`) - // because it breaks for some reason. I (@jyn514) haven't taken time to track down the bug - // because rust-lang/rust uses RUSTFLAGS and nearly no one else uses --sysroot. -} From 616b6d2be13092b53b83cd4e53e5e88f310a86fc Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 12 Jan 2023 19:00:16 +0100 Subject: [PATCH 451/524] Bump nightly version -> 2023-01-12 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 9399d422036d..40a6f47095ec 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-12-29" +channel = "nightly-2023-01-12" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From cd76d574e444197d692d8652c27f869038430af1 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 12 Jan 2023 19:12:06 +0100 Subject: [PATCH 452/524] Also add rustc_driver to clippy_utils I'm not sure why this is necessary. It worked without this for me locally, but this fails in CI. The same was done in clippy_dev --- clippy_utils/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 991be9727c21..7a4a9036dd36 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -22,6 +22,9 @@ extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; extern crate rustc_data_structures; +// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate. +#[allow(unused_extern_crates)] +extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_typeck; From 5eed9c69ca9fa04a1417f1f14df0bb5bab2fc8c8 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 12 Jan 2023 13:28:22 -0500 Subject: [PATCH 453/524] Revert 4dbd8ad34e7f6820f6e9e99531353e7ffe37b76a, c7dc96155853a3919b973347277d0e9bcaaa22f0, ed519ad746e31f64c4e9255be561785612532d37 and c6477eb71188311f01f409da628fab7062697bd7 --- clippy_lints/src/dereference.rs | 8 +- clippy_lints/src/redundant_clone.rs | 4 +- clippy_utils/src/mir/possible_borrower.rs | 271 ++++++++-------------- tests/ui/needless_borrow.fixed | 13 +- tests/ui/needless_borrow.rs | 13 +- tests/ui/needless_borrow.stderr | 8 +- tests/ui/redundant_clone.fixed | 6 - tests/ui/redundant_clone.rs | 6 - tests/ui/redundant_clone.stderr | 14 +- 9 files changed, 109 insertions(+), 234 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 728941b8b3d9..7b43d8ccc67d 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1282,10 +1282,10 @@ fn referent_used_exactly_once<'tcx>( possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); } let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; - // If `place.local` were not included here, the `copyable_iterator::warn` test would fail. The - // reason is that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible - // borrower of itself. See the comment in that method for an explanation as to why. - possible_borrower.at_most_borrowers(cx, &[local, place.local], place.local, location) + // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is + // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of + // itself. See the comment in that method for an explanation as to why. + possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) && used_exactly_once(mir, place.local).unwrap_or(false) } else { false diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 0e7c5cca7240..c1677fb3da1c 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { // `res = clone(arg)` can be turned into `res = move arg;` // if `arg` is the only borrow of `cloned` at this point. - if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg], cloned, loc) { + if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) { continue; } @@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { // StorageDead(pred_arg); // res = to_path_buf(cloned); // ``` - if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg, cloned], local, loc) { + if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) { continue; } diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 395d46e7a2f8..8c695801c73f 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -1,137 +1,89 @@ -use super::possible_origin::PossibleOriginVisitor; +use super::{possible_origin::PossibleOriginVisitor, transitive_relation::TransitiveRelation}; use crate::ty::is_copy; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::FxHashMap; use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::LateContext; -use rustc_middle::mir::{ - self, visit::Visitor as _, BasicBlock, Local, Location, Mutability, Statement, StatementKind, Terminator, -}; -use rustc_middle::ty::{self, visit::TypeVisitor, TyCtxt}; -use rustc_mir_dataflow::{ - fmt::DebugWithContext, impls::MaybeStorageLive, lattice::JoinSemiLattice, Analysis, AnalysisDomain, - CallReturnPlaces, ResultsCursor, -}; -use std::borrow::Cow; +use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; +use rustc_middle::ty::{self, visit::TypeVisitor}; +use rustc_mir_dataflow::{impls::MaybeStorageLive, Analysis, ResultsCursor}; use std::ops::ControlFlow; /// Collects the possible borrowers of each local. /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. #[allow(clippy::module_name_repetitions)] -struct PossibleBorrowerAnalysis<'b, 'tcx> { - tcx: TyCtxt<'tcx>, +struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { + possible_borrower: TransitiveRelation, body: &'b mir::Body<'tcx>, + cx: &'a LateContext<'tcx>, possible_origin: FxHashMap>, } -#[derive(Clone, Debug, Eq, PartialEq)] -struct PossibleBorrowerState { - map: FxIndexMap>, - domain_size: usize, -} - -impl PossibleBorrowerState { - fn new(domain_size: usize) -> Self { +impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn new( + cx: &'a LateContext<'tcx>, + body: &'b mir::Body<'tcx>, + possible_origin: FxHashMap>, + ) -> Self { Self { - map: FxIndexMap::default(), - domain_size, + possible_borrower: TransitiveRelation::default(), + cx, + body, + possible_origin, } } - #[allow(clippy::similar_names)] - fn add(&mut self, borrowed: Local, borrower: Local) { - self.map - .entry(borrowed) - .or_insert(BitSet::new_empty(self.domain_size)) - .insert(borrower); - } -} - -impl DebugWithContext for PossibleBorrowerState { - fn fmt_with(&self, _ctxt: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - <_ as std::fmt::Debug>::fmt(self, f) - } - fn fmt_diff_with(&self, _old: &Self, _ctxt: &C, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - unimplemented!() - } -} + fn into_map( + self, + cx: &'a LateContext<'tcx>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + ) -> PossibleBorrowerMap<'b, 'tcx> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } -impl JoinSemiLattice for PossibleBorrowerState { - fn join(&mut self, other: &Self) -> bool { - let mut changed = false; - for (&borrowed, borrowers) in other.map.iter() { + let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); if !borrowers.is_empty() { - changed |= self - .map - .entry(borrowed) - .or_insert(BitSet::new_empty(self.domain_size)) - .union(borrowers); + map.insert(row, borrowers); } } - changed - } -} - -impl<'b, 'tcx> AnalysisDomain<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { - type Domain = PossibleBorrowerState; - - const NAME: &'static str = "possible_borrower"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - PossibleBorrowerState::new(body.local_decls.len()) - } - - fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _entry_set: &mut Self::Domain) {} -} -impl<'b, 'tcx> PossibleBorrowerAnalysis<'b, 'tcx> { - fn new( - tcx: TyCtxt<'tcx>, - body: &'b mir::Body<'tcx>, - possible_origin: FxHashMap>, - ) -> Self { - Self { - tcx, - body, - possible_origin, + let bs = BitSet::new_empty(self.body.local_decls.len()); + PossibleBorrowerMap { + map, + maybe_live, + bitset: (bs.clone(), bs), } } } -impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { - fn apply_call_return_effect( - &self, - _state: &mut Self::Domain, - _block: BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - } - - fn apply_statement_effect(&self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location) { - if let StatementKind::Assign(box (place, rvalue)) = &statement.kind { - let lhs = place.local; - match rvalue { - mir::Rvalue::Ref(_, _, borrowed) => { - state.add(borrowed.local, lhs); - }, - other => { - if ContainsRegion - .visit_ty(place.ty(&self.body.local_decls, self.tcx).ty) - .is_continue() - { - return; +impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + self.possible_borrower.add(borrowed.local, lhs); + }, + other => { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) + .is_continue() + { + return; + } + rvalue_locals(other, |rhs| { + if lhs != rhs { + self.possible_borrower.add(rhs, lhs); } - rvalue_locals(other, |rhs| { - if lhs != rhs { - state.add(rhs, lhs); - } - }); - }, - } + }); + }, } } - fn apply_terminator_effect(&self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location) { + fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { if let mir::TerminatorKind::Call { args, destination: mir::Place { local: dest, .. }, @@ -171,10 +123,10 @@ impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { for y in mutable_variables { for x in &immutable_borrowers { - state.add(*x, y); + self.possible_borrower.add(*x, y); } for x in &mutable_borrowers { - state.add(*x, y); + self.possible_borrower.add(*x, y); } } } @@ -210,98 +162,73 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { } } -/// Result of `PossibleBorrowerAnalysis`. +/// Result of `PossibleBorrowerVisitor`. #[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { - body: &'b mir::Body<'tcx>, - possible_borrower: ResultsCursor<'b, 'tcx, PossibleBorrowerAnalysis<'b, 'tcx>>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'b>>, - pushed: BitSet, - stack: Vec, + /// Mapping `Local -> its possible borrowers` + pub map: FxHashMap>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + // Caches to avoid allocation of `BitSet` on every query + pub bitset: (BitSet, BitSet), } -impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { - pub fn new(cx: &LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { +impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { let possible_origin = { let mut vis = PossibleOriginVisitor::new(mir); vis.visit_body(mir); vis.into_map(cx) }; - let possible_borrower = PossibleBorrowerAnalysis::new(cx.tcx, mir, possible_origin) + let maybe_storage_live_result = MaybeStorageLive::new(BitSet::new_empty(mir.local_decls.len())) .into_engine(cx.tcx, mir) - .pass_name("possible_borrower") + .pass_name("redundant_clone") .iterate_to_fixpoint() .into_results_cursor(mir); - let maybe_live = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) - .into_engine(cx.tcx, mir) - .pass_name("possible_borrower") - .iterate_to_fixpoint() - .into_results_cursor(mir); - PossibleBorrowerMap { - body: mir, - possible_borrower, - maybe_live, - pushed: BitSet::new_empty(mir.local_decls.len()), - stack: Vec::with_capacity(mir.local_decls.len()), - } + let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); + vis.visit_body(mir); + vis.into_map(cx, maybe_storage_live_result) } - /// Returns true if the set of borrowers of `borrowed` living at `at` includes no more than - /// `borrowers`. - /// Notes: - /// 1. It would be nice if `PossibleBorrowerMap` could store `cx` so that `at_most_borrowers` - /// would not require it to be passed in. But a `PossibleBorrowerMap` is stored in `LintPass` - /// `Dereferencing`, which outlives any `LateContext`. - /// 2. In all current uses of `at_most_borrowers`, `borrowers` is a slice of at most two - /// elements. Thus, `borrowers.contains(...)` is effectively a constant-time operation. If - /// `at_most_borrowers`'s uses were to expand beyond this, its implementation might have to be - /// adjusted. - pub fn at_most_borrowers( + /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. + pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { + self.bounded_borrowers(borrowers, borrowers, borrowed, at) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` + /// but no more than `above`. + pub fn bounded_borrowers( &mut self, - cx: &LateContext<'tcx>, - borrowers: &[mir::Local], + below: &[mir::Local], + above: &[mir::Local], borrowed: mir::Local, at: mir::Location, ) -> bool { - if is_copy(cx, self.body.local_decls[borrowed].ty) { - return true; - } - - self.possible_borrower.seek_before_primary_effect(at); - self.maybe_live.seek_before_primary_effect(at); - - let possible_borrower = &self.possible_borrower.get().map; - let maybe_live = &self.maybe_live; - - self.pushed.clear(); - self.stack.clear(); + self.maybe_live.seek_after_primary_effect(at); - if let Some(borrowers) = possible_borrower.get(&borrowed) { - for b in borrowers.iter() { - if self.pushed.insert(b) { - self.stack.push(b); - } + self.bitset.0.clear(); + let maybe_live = &mut self.maybe_live; + if let Some(bitset) = self.map.get(&borrowed) { + for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { + self.bitset.0.insert(b); } } else { - // Nothing borrows `borrowed` at `at`. - return true; + return false; } - while let Some(borrower) = self.stack.pop() { - if maybe_live.contains(borrower) && !borrowers.contains(&borrower) { - return false; - } + self.bitset.1.clear(); + for b in below { + self.bitset.1.insert(*b); + } - if let Some(borrowers) = possible_borrower.get(&borrower) { - for b in borrowers.iter() { - if self.pushed.insert(b) { - self.stack.push(b); - } - } - } + if !self.bitset.0.superset(&self.bitset.1) { + return false; + } + + for b in above { + self.bitset.0.remove(*b); } - true + self.bitset.0.is_empty() } pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 31e1cb6c3d7f..4cb7f6b687f1 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes, lint_reasons, rustc_private)] +#![feature(lint_reasons)] #![allow( unused, clippy::uninlined_format_args, @@ -491,14 +491,3 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } - -extern crate rustc_lint; -extern crate rustc_span; - -#[allow(dead_code)] -mod span_lint { - use rustc_lint::{LateContext, Lint, LintContext}; - fn foo(cx: &LateContext<'_>, lint: &'static Lint) { - cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(String::new())); - } -} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 55c2738fcf27..9a01190ed8db 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes, lint_reasons, rustc_private)] +#![feature(lint_reasons)] #![allow( unused, clippy::uninlined_format_args, @@ -491,14 +491,3 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } - -extern crate rustc_lint; -extern crate rustc_span; - -#[allow(dead_code)] -mod span_lint { - use rustc_lint::{LateContext, Lint, LintContext}; - fn foo(cx: &LateContext<'_>, lint: &'static Lint) { - cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); - } -} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 98a48d68317b..d26c317124b8 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -216,11 +216,5 @@ error: the borrowed expression implements the required traits LL | foo(&a); | ^^ help: change this to: `a` -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:502:85 - | -LL | cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); - | ^^^^^^^^^^^^^^ help: change this to: `String::new()` - -error: aborting due to 37 previous errors +error: aborting due to 36 previous errors diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index a157b6a6f9ad..00b427450935 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -239,9 +239,3 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } - -#[allow(unused, clippy::manual_retain)] -fn possible_borrower_improvements() { - let mut s = String::from("foobar"); - s = s.chars().filter(|&c| c != 'o').collect(); -} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index 430672e8b8df..f899127db8d0 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -239,9 +239,3 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } - -#[allow(unused, clippy::manual_retain)] -fn possible_borrower_improvements() { - let mut s = String::from("foobar"); - s = s.chars().filter(|&c| c != 'o').to_owned().collect(); -} diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 1bacc2c76af1..782590034d05 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -179,17 +179,5 @@ note: this value is dropped without further use LL | foo(&x.clone(), move || { | ^ -error: redundant clone - --> $DIR/redundant_clone.rs:246:40 - | -LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); - | ^^^^^^^^^^^ help: remove this - | -note: this value is dropped without further use - --> $DIR/redundant_clone.rs:246:9 - | -LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 16 previous errors +error: aborting due to 15 previous errors From 757e944ba6ace471727cb320c75a52b321c05322 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 12 Jan 2023 13:29:23 -0500 Subject: [PATCH 454/524] Adjust old code for newer rustc version. --- clippy_utils/src/mir/possible_borrower.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 8c695801c73f..9adae7733894 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -6,6 +6,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; use rustc_middle::ty::{self, visit::TypeVisitor}; use rustc_mir_dataflow::{impls::MaybeStorageLive, Analysis, ResultsCursor}; +use std::borrow::Cow; use std::ops::ControlFlow; /// Collects the possible borrowers of each local. @@ -36,7 +37,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { fn into_map( self, cx: &'a LateContext<'tcx>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, ) -> PossibleBorrowerMap<'b, 'tcx> { let mut map = FxHashMap::default(); for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { @@ -167,7 +168,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { pub struct PossibleBorrowerMap<'b, 'tcx> { /// Mapping `Local -> its possible borrowers` pub map: FxHashMap>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, // Caches to avoid allocation of `BitSet` on every query pub bitset: (BitSet, BitSet), } @@ -179,7 +180,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive::new(BitSet::new_empty(mir.local_decls.len())) + let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) .into_engine(cx.tcx, mir) .pass_name("redundant_clone") .iterate_to_fixpoint() From d21616737b56681fdf19cdf9a9f8db16d0e87961 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 12 Jan 2023 19:48:13 +0100 Subject: [PATCH 455/524] Merge commit '7f27e2e74ef957baa382dc05cf08df6368165c74' into clippyup --- .github/driver.sh | 7 + CHANGELOG.md | 1 + book/src/installation.md | 2 +- clippy_dev/src/lib.rs | 3 + clippy_lints/src/box_default.rs | 4 +- clippy_lints/src/copies.rs | 6 +- clippy_lints/src/dbg_macro.rs | 10 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/default.rs | 7 +- clippy_lints/src/dereference.rs | 8 +- clippy_lints/src/derivable_impls.rs | 161 ++++-- clippy_lints/src/derive.rs | 31 +- clippy_lints/src/drop_forget_ref.rs | 2 +- .../src/empty_structs_with_brackets.rs | 2 +- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/loops/needless_range_loop.rs | 4 +- clippy_lints/src/loops/single_element_loop.rs | 2 + ...se_sensitive_file_extension_comparisons.rs | 46 +- clippy_lints/src/methods/clone_on_copy.rs | 14 +- clippy_lints/src/methods/filter_map.rs | 174 +++---- clippy_lints/src/methods/iter_kv_map.rs | 22 +- .../src/methods/suspicious_to_owned.rs | 6 +- clippy_lints/src/mutex_atomic.rs | 10 +- .../src/operators/arithmetic_side_effects.rs | 11 +- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/renamed_lints.rs | 1 + clippy_lints/src/returns.rs | 33 +- clippy_lints/src/types/mod.rs | 2 +- clippy_lints/src/unused_self.rs | 21 +- .../internal_lints/metadata_collector.rs | 2 +- clippy_utils/src/lib.rs | 37 +- clippy_utils/src/mir/possible_borrower.rs | 270 ++++------ clippy_utils/src/msrvs.rs | 4 +- clippy_utils/src/paths.rs | 1 - clippy_utils/src/visitors.rs | 11 + rust-toolchain | 2 +- src/driver.rs | 5 +- tests/integration.rs | 9 + .../arithmetic_side_effects_allowed.rs | 2 +- tests/ui-toml/dbg_macro/dbg_macro.stderr | 36 +- tests/ui/arithmetic_side_effects.rs | 225 ++++++-- tests/ui/arithmetic_side_effects.stderr | 480 ++++++++++++++---- tests/ui/box_default.fixed | 18 +- tests/ui/box_default.rs | 10 + tests/ui/box_default.stderr | 16 +- ...sensitive_file_extension_comparisons.fixed | 67 +++ ...se_sensitive_file_extension_comparisons.rs | 13 +- ...ensitive_file_extension_comparisons.stderr | 66 ++- tests/ui/clone_on_copy.stderr | 2 +- tests/ui/dbg_macro.stderr | 52 +- tests/ui/default_trait_access.fixed | 8 +- tests/ui/default_trait_access.stderr | 16 +- tests/ui/derivable_impls.fixed | 21 + tests/ui/derivable_impls.rs | 23 + tests/ui/derivable_impls.stderr | 23 +- tests/ui/derive.rs | 11 + tests/ui/derive_hash_xor_eq.stderr | 59 --- ...r_eq.rs => derived_hash_with_manual_eq.rs} | 21 +- tests/ui/derived_hash_with_manual_eq.stderr | 29 ++ tests/ui/drop_ref.rs | 23 + tests/ui/drop_ref.stderr | 38 +- tests/ui/field_reassign_with_default.rs | 21 + tests/ui/iter_kv_map.fixed | 35 +- tests/ui/iter_kv_map.rs | 39 +- tests/ui/iter_kv_map.stderr | 114 ++++- tests/ui/needless_borrow.fixed | 13 +- tests/ui/needless_borrow.rs | 13 +- tests/ui/needless_borrow.stderr | 8 +- tests/ui/needless_return.fixed | 10 + tests/ui/needless_return.rs | 10 + tests/ui/needless_return.stderr | 18 +- tests/ui/redundant_clone.fixed | 6 - tests/ui/redundant_clone.rs | 6 - tests/ui/redundant_clone.stderr | 14 +- tests/ui/rename.fixed | 2 + tests/ui/rename.rs | 2 + tests/ui/rename.stderr | 90 ++-- tests/ui/single_element_loop.fixed | 27 + tests/ui/single_element_loop.rs | 26 + tests/ui/single_element_loop.stderr | 29 +- tests/ui/suspicious_to_owned.stderr | 8 +- tests/ui/unnecessary_clone.stderr | 10 +- tests/ui/unused_self.rs | 10 + tests/ui/unused_self.stderr | 18 +- 85 files changed, 1879 insertions(+), 850 deletions(-) create mode 100644 tests/ui/case_sensitive_file_extension_comparisons.fixed delete mode 100644 tests/ui/derive_hash_xor_eq.stderr rename tests/ui/{derive_hash_xor_eq.rs => derived_hash_with_manual_eq.rs} (62%) create mode 100644 tests/ui/derived_hash_with_manual_eq.stderr diff --git a/.github/driver.sh b/.github/driver.sh index 6ff189fc8592..798782340ee7 100644 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -17,6 +17,13 @@ test "$sysroot" = $desired_sysroot sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot) test "$sysroot" = $desired_sysroot +# Check that the --sysroot argument is only passed once (SYSROOT is ignored) +( + cd rustc_tools_util + touch src/lib.rs + SYSROOT=/tmp RUSTFLAGS="--sysroot=$(rustc --print sysroot)" ../target/debug/cargo-clippy clippy --verbose +) + # Make sure this isn't set - clippy-driver should cope without it unset CARGO_MANIFEST_DIR diff --git a/CHANGELOG.md b/CHANGELOG.md index 02f3188f8be0..8e31e8f0d981 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4137,6 +4137,7 @@ Released 2018-09-13 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq +[`derived_hash_with_manual_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derived_hash_with_manual_eq [`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods diff --git a/book/src/installation.md b/book/src/installation.md index b2a28d0be622..cce888b17d4d 100644 --- a/book/src/installation.md +++ b/book/src/installation.md @@ -1,6 +1,6 @@ # Installation -If you're using `rustup` to install and manage you're Rust toolchains, Clippy is +If you're using `rustup` to install and manage your Rust toolchains, Clippy is usually **already installed**. In that case you can skip this chapter and go to the [Usage] chapter. diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 80bb83af43b1..e70488165b99 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -5,6 +5,9 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] +// The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate. +#[allow(unused_extern_crates)] +extern crate rustc_driver; extern crate rustc_lexer; use std::path::PathBuf; diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index 91900542af83..9d98a6bab710 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -8,7 +8,7 @@ use rustc_hir::{ Block, Expr, ExprKind, Local, Node, QPath, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; +use rustc_middle::{lint::in_external_macro, ty::print::with_forced_trimmed_paths}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -59,7 +59,7 @@ impl LateLintPass<'_> for BoxDefault { if is_plain_default(arg_path) || given_type(cx, expr) { "Box::default()".into() } else { - format!("Box::<{arg_ty}>::default()") + with_forced_trimmed_paths!(format!("Box::<{arg_ty}>::default()")) }, Applicability::MachineApplicable ); diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 0e3d9317590f..f10c35cde52a 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -525,7 +525,11 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo .iter() .filter(|&&(_, name)| !name.as_str().starts_with('_')) .any(|&(_, name)| { - let mut walker = ContainsName { name, result: false }; + let mut walker = ContainsName { + name, + result: false, + cx, + }; // Scan block block diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index fe9f4f9ae3cb..799e71e847a9 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -10,11 +10,11 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// Checks for usage of dbg!() macro. + /// Checks for usage of the [`dbg!`](https://doc.rust-lang.org/std/macro.dbg.html) macro. /// /// ### Why is this bad? - /// `dbg!` macro is intended as a debugging tool. It - /// should not be in version control. + /// The `dbg!` macro is intended as a debugging tool. It should not be present in released + /// software or committed to a version control system. /// /// ### Example /// ```rust,ignore @@ -91,8 +91,8 @@ impl LateLintPass<'_> for DbgMacro { cx, DBG_MACRO, macro_call.span, - "`dbg!` macro is intended as a debugging tool", - "ensure to avoid having uses of it in version control", + "the `dbg!` macro is intended as a debugging tool", + "remove the invocation before committing it to a version control system", suggestion, applicability, ); diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2982460c9cfa..91ca73633f06 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -111,7 +111,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::dereference::NEEDLESS_BORROW_INFO, crate::dereference::REF_BINDING_TO_REFERENCE_INFO, crate::derivable_impls::DERIVABLE_IMPLS_INFO, - crate::derive::DERIVE_HASH_XOR_EQ_INFO, + crate::derive::DERIVED_HASH_WITH_MANUAL_EQ_INFO, crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO, crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO, crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO, diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 7f937de1dd31..a04693f4637a 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -11,6 +11,7 @@ use rustc_hir::def::Res; 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_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -98,9 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { if let ty::Adt(def, ..) = expr_ty.kind(); if !is_from_proc_macro(cx, expr); then { - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did())); + let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did()))); span_lint_and_sugg( cx, DEFAULT_TRAIT_ACCESS, @@ -170,7 +169,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { // find out if and which field was set by this `consecutive_statement` if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) { // interrupt and cancel lint if assign_rhs references the original binding - if contains_name(binding_name, assign_rhs) { + if contains_name(binding_name, assign_rhs, cx) { cancel_lint = true; break; } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index f327c9a71b3c..05f2b92c0370 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1282,10 +1282,10 @@ fn referent_used_exactly_once<'tcx>( possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir))); } let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1; - // If `place.local` were not included here, the `copyable_iterator::warn` test would fail. The - // reason is that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible - // borrower of itself. See the comment in that method for an explanation as to why. - possible_borrower.at_most_borrowers(cx, &[local, place.local], place.local, location) + // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is + // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of + // itself. See the comment in that method for an explanation as to why. + possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location) && used_exactly_once(mir, place.local).unwrap_or(false) } else { false diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index ae8f6b794499..bc18e2e5ed5f 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -1,12 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::indent_of; use clippy_utils::{is_default_equivalent, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{ - def::{DefKind, Res}, - Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind, + def::{CtorKind, CtorOf, DefKind, Res}, + Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_middle::ty::{AdtDef, DefIdTree}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; declare_clippy_lint! { @@ -51,7 +54,18 @@ declare_clippy_lint! { "manual implementation of the `Default` trait which is equal to a derive" } -declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); +pub struct DerivableImpls { + msrv: Msrv, +} + +impl DerivableImpls { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + DerivableImpls { msrv } + } +} + +impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); fn is_path_self(e: &Expr<'_>) -> bool { if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind { @@ -61,6 +75,98 @@ fn is_path_self(e: &Expr<'_>) -> bool { } } +fn check_struct<'tcx>( + cx: &LateContext<'tcx>, + item: &'tcx Item<'_>, + self_ty: &Ty<'_>, + func_expr: &Expr<'_>, + adt_def: AdtDef<'_>, +) { + if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { + if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() { + for arg in a.args { + if !matches!(arg, GenericArg::Lifetime(_)) { + return; + } + } + } + } + let should_emit = match peel_blocks(func_expr).kind { + ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)), + ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)), + ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)), + _ => false, + }; + + if should_emit { + let struct_span = cx.tcx.def_span(adt_def.did()); + span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| { + diag.span_suggestion_hidden( + item.span, + "remove the manual implementation...", + String::new(), + Applicability::MachineApplicable, + ); + diag.span_suggestion( + struct_span.shrink_to_lo(), + "...and instead derive it", + "#[derive(Default)]\n".to_string(), + Applicability::MachineApplicable, + ); + }); + } +} + +fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) { + if_chain! { + if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind; + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res; + if let variant_id = cx.tcx.parent(id); + if let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id); + if variant_def.fields.is_empty(); + if !variant_def.is_field_list_non_exhaustive(); + + then { + let enum_span = cx.tcx.def_span(adt_def.did()); + let indent_enum = indent_of(cx, enum_span).unwrap_or(0); + let variant_span = cx.tcx.def_span(variant_def.def_id); + let indent_variant = indent_of(cx, variant_span).unwrap_or(0); + span_lint_and_then( + cx, + DERIVABLE_IMPLS, + item.span, + "this `impl` can be derived", + |diag| { + diag.span_suggestion_hidden( + item.span, + "remove the manual implementation...", + String::new(), + Applicability::MachineApplicable + ); + diag.span_suggestion( + enum_span.shrink_to_lo(), + "...and instead derive it...", + format!( + "#[derive(Default)]\n{indent}", + indent = " ".repeat(indent_enum), + ), + Applicability::MachineApplicable + ); + diag.span_suggestion( + variant_span.shrink_to_lo(), + "...and mark the default variant", + format!( + "#[default]\n{indent}", + indent = " ".repeat(indent_variant), + ), + Applicability::MachineApplicable + ); + } + ); + } + } +} + impl<'tcx> LateLintPass<'tcx> for DerivableImpls { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { @@ -83,49 +189,16 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if !attrs.iter().any(|attr| attr.doc_str().is_some()); if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); if !child_attrs.iter().any(|attr| attr.doc_str().is_some()); - if adt_def.is_struct(); - then { - if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { - if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() { - for arg in a.args { - if !matches!(arg, GenericArg::Lifetime(_)) { - return; - } - } - } - } - let should_emit = match peel_blocks(func_expr).kind { - ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)), - ExprKind::Call(callee, args) - if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)), - ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)), - _ => false, - }; - if should_emit { - let struct_span = cx.tcx.def_span(adt_def.did()); - span_lint_and_then( - cx, - DERIVABLE_IMPLS, - item.span, - "this `impl` can be derived", - |diag| { - diag.span_suggestion_hidden( - item.span, - "remove the manual implementation...", - String::new(), - Applicability::MachineApplicable - ); - diag.span_suggestion( - struct_span.shrink_to_lo(), - "...and instead derive it", - "#[derive(Default)]\n".to_string(), - Applicability::MachineApplicable - ); - } - ); + then { + if adt_def.is_struct() { + check_struct(cx, item, self_ty, func_expr, adt_def); + } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { + check_enum(cx, item, func_expr, adt_def); } } } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index cf3483d4c00b..f4b15e0916db 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -14,8 +14,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, - Ty, TyCtxt, + self, Binder, BoundConstness, Clause, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, + TraitPredicate, Ty, TyCtxt, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -46,7 +46,7 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "pre 1.29.0"] - pub DERIVE_HASH_XOR_EQ, + pub DERIVED_HASH_WITH_MANUAL_EQ, correctness, "deriving `Hash` but implementing `PartialEq` explicitly" } @@ -197,7 +197,7 @@ declare_clippy_lint! { declare_lint_pass!(Derive => [ EXPL_IMPL_CLONE_ON_COPY, - DERIVE_HASH_XOR_EQ, + DERIVED_HASH_WITH_MANUAL_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, UNSAFE_DERIVE_DESERIALIZE, DERIVE_PARTIAL_EQ_WITHOUT_EQ @@ -226,7 +226,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { } } -/// Implementation of the `DERIVE_HASH_XOR_EQ` lint. +/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. fn check_hash_peq<'tcx>( cx: &LateContext<'tcx>, span: Span, @@ -243,7 +243,7 @@ fn check_hash_peq<'tcx>( cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); - if peq_is_automatically_derived == hash_is_automatically_derived { + if !hash_is_automatically_derived || peq_is_automatically_derived { return; } @@ -252,17 +252,11 @@ fn check_hash_peq<'tcx>( // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] if trait_ref.substs.type_at(1) == ty { - let mess = if peq_is_automatically_derived { - "you are implementing `Hash` explicitly but have derived `PartialEq`" - } else { - "you are deriving `Hash` but have implemented `PartialEq` explicitly" - }; - span_lint_and_then( cx, - DERIVE_HASH_XOR_EQ, + DERIVED_HASH_WITH_MANUAL_EQ, span, - mess, + "you are deriving `Hash` but have implemented `PartialEq` explicitly", |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); @@ -366,6 +360,15 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { return; } + // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. + // https://github.com/rust-lang/rust-clippy/issues/10188 + if ty_adt.repr().packed() + && ty_subs + .iter() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) + { + return; + } span_lint_and_note( cx, diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 4721a7b37056..11e1bcdf12d1 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { let is_copy = is_copy(cx, arg_ty); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let (lint, msg) = match fn_name { - sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY), + sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => (DROP_REF, DROP_REF_SUMMARY), sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY), sym::mem_drop if is_copy && !drop_is_single_call_in_arm => (DROP_COPY, DROP_COPY_SUMMARY), sym::mem_forget if is_copy => (FORGET_COPY, FORGET_COPY_SUMMARY), diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index 08bf80a42290..c3a020433de8 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -45,7 +45,7 @@ impl EarlyLintPass for EmptyStructsWithBrackets { span_after_ident, "remove the brackets", ";", - Applicability::MachineApplicable); + Applicability::Unspecified); }, ); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dcd8ca81ae87..d8e2ae02c5a6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -639,7 +639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented)); store.register_late_pass(|_| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|_| Box::new(derive::Derive)); - store.register_late_pass(|_| Box::new(derivable_impls::DerivableImpls)); + store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(msrv()))); store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 27ba27202bf7..3bca93d80aa7 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -81,7 +81,7 @@ pub(super) fn check<'tcx>( let skip = if starts_at_zero { String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) { + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) { return; } else { format!(".skip({})", snippet(cx, start.span, "..")) @@ -109,7 +109,7 @@ pub(super) fn check<'tcx>( if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) { return; } else { match limits { diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index f4b47808dfaa..744fd61bd135 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -1,6 +1,7 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet_with_applicability}; +use clippy_utils::visitors::contains_break_or_continue; use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::Mutability; @@ -67,6 +68,7 @@ pub(super) fn check<'tcx>( if_chain! { if let ExprKind::Block(block, _) = body.kind; if !block.stmts.is_empty(); + if !contains_break_or_continue(body); then { let mut applicability = Applicability::MachineApplicable; let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index d226c0bba659..0b3bf22743fa 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,7 +1,10 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; +use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_span::{source_map::Spanned, Span}; @@ -15,6 +18,15 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, ) { + if let ExprKind::MethodCall(path_segment, ..) = recv.kind { + if matches!( + path_segment.ident.name.as_str(), + "to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase" + ) { + return; + } + } + if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); @@ -28,13 +40,37 @@ pub(super) fn check<'tcx>( let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs(); if recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String); then { - span_lint_and_help( + span_lint_and_then( cx, CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - call_span, + recv.span.to(call_span), "case-sensitive file extension comparison", - None, - "consider using a case-insensitive comparison instead", + |diag| { + diag.help("consider using a case-insensitive comparison instead"); + if let Some(mut recv_source) = snippet_opt(cx, recv.span) { + + if !cx.typeck_results().expr_ty(recv).is_ref() { + recv_source = format!("&{recv_source}"); + } + + let suggestion_source = reindent_multiline( + format!( + "std::path::Path::new({}) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", + recv_source, ext_str.strip_prefix('.').unwrap()).into(), + true, + Some(indent_of(cx, call_span).unwrap_or(0) + 4) + ); + + diag.span_suggestion( + recv.span.to(call_span), + "use std::path::Path", + suggestion_source, + Applicability::MaybeIncorrect, + ); + } + } ); } } diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 7c7938dd2e8b..3795c0ec2509 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, adjustment::Adjust}; +use rustc_middle::ty::{self, adjustment::Adjust, print::with_forced_trimmed_paths}; use rustc_span::symbol::{sym, Symbol}; use super::CLONE_DOUBLE_REF; @@ -47,10 +47,10 @@ pub(super) fn check( cx, CLONE_DOUBLE_REF, expr.span, - &format!( + &with_forced_trimmed_paths!(format!( "using `clone` on a double-reference; \ this will copy the reference of type `{ty}` instead of cloning the inner type" - ), + )), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; @@ -61,11 +61,11 @@ pub(super) fn check( } let refs = "&".repeat(n + 1); let derefs = "*".repeat(n); - let explicit = format!("<{refs}{ty}>::clone({snip})"); + let explicit = with_forced_trimmed_paths!(format!("<{refs}{ty}>::clone({snip})")); diag.span_suggestion( expr.span, "try dereferencing it", - format!("{refs}({derefs}{}).clone()", snip.deref()), + with_forced_trimmed_paths!(format!("{refs}({derefs}{}).clone()", snip.deref())), Applicability::MaybeIncorrect, ); diag.span_suggestion( @@ -129,7 +129,9 @@ pub(super) fn check( cx, CLONE_ON_COPY, expr.span, - &format!("using `clone` on type `{ty}` which implements the `Copy` trait"), + &with_forced_trimmed_paths!(format!( + "using `clone` on type `{ty}` which implements the `Copy` trait" + )), help, sugg, app, diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index f888c58a72de..fc80f2eeae01 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -30,12 +30,12 @@ fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> match closure_expr.kind { hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => { if_chain! { - if ident.name == method_name; - if let hir::ExprKind::Path(path) = &receiver.kind; - if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id); - then { - return arg_id == *local - } + if ident.name == method_name; + if let hir::ExprKind::Path(path) = &receiver.kind; + if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id); + then { + return arg_id == *local + } } false }, @@ -92,92 +92,92 @@ pub(super) fn check( } if_chain! { - if is_trait_method(cx, map_recv, sym::Iterator); - - // filter(|x| ...is_some())... - if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind; - let filter_body = cx.tcx.hir().body(filter_body_id); - if let [filter_param] = filter_body.params; - // optional ref pattern: `filter(|&x| ..)` - let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { - (ref_pat, true) - } else { - (filter_param.pat, false) + if is_trait_method(cx, map_recv, sym::Iterator); + + // filter(|x| ...is_some())... + if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind; + let filter_body = cx.tcx.hir().body(filter_body_id); + if let [filter_param] = filter_body.params; + // optional ref pattern: `filter(|&x| ..)` + let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + (ref_pat, true) + } else { + (filter_param.pat, false) + }; + // closure ends with is_some() or is_ok() + if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; + if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind; + if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def(); + if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) { + Some(false) + } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) { + Some(true) + } else { + None + }; + if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; + + // ...map(|x| ...unwrap()) + if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind; + let map_body = cx.tcx.hir().body(map_body_id); + if let [map_param] = map_body.params; + if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; + // closure ends with expect() or unwrap() + if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); + + // .filter(..).map(|y| f(y).copied().unwrap()) + // ~~~~ + let map_arg_peeled = match map_arg.kind { + ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => { + original_arg + }, + _ => map_arg, + }; + + // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap()) + let simple_equal = path_to_local_id(filter_arg, filter_param_id) + && path_to_local_id(map_arg_peeled, map_param_id); + + let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + // in `filter(|x| ..)`, replace `*x` with `x` + let a_path = if_chain! { + if !is_filter_param_ref; + if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; + then { expr_path } else { a } }; - // closure ends with is_some() or is_ok() - if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; - if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind; - if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def(); - if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) { - Some(false) - } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) { - Some(true) + // let the filter closure arg and the map closure arg be equal + path_to_local_id(a_path, filter_param_id) + && path_to_local_id(b, map_param_id) + && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) + }; + + if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled); + then { + let span = filter_span.with_hi(expr.span.hi()); + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) } else { - None - }; - if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; - - // ...map(|x| ...unwrap()) - if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind; - let map_body = cx.tcx.hir().body(map_body_id); - if let [map_param] = map_body.params; - if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; - // closure ends with expect() or unwrap() - if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind; - if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); - - // .filter(..).map(|y| f(y).copied().unwrap()) - // ~~~~ - let map_arg_peeled = match map_arg.kind { - ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => { - original_arg - }, - _ => map_arg, + ("filter", MANUAL_FILTER_MAP) }; + let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); + let (to_opt, deref) = if is_result { + (".ok()", String::new()) + } else { + let derefs = cx.typeck_results() + .expr_adjustments(map_arg) + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); - // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap()) - let simple_equal = path_to_local_id(filter_arg, filter_param_id) - && path_to_local_id(map_arg_peeled, map_param_id); - - let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - // in `filter(|x| ..)`, replace `*x` with `x` - let a_path = if_chain! { - if !is_filter_param_ref; - if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; - then { expr_path } else { a } - }; - // let the filter closure arg and the map closure arg be equal - path_to_local_id(a_path, filter_param_id) - && path_to_local_id(b, map_param_id) - && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) + ("", "*".repeat(derefs)) }; - - if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled); - then { - let span = filter_span.with_hi(expr.span.hi()); - let (filter_name, lint) = if is_find { - ("find", MANUAL_FIND_MAP) - } else { - ("filter", MANUAL_FILTER_MAP) - }; - let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); - let (to_opt, deref) = if is_result { - (".ok()", String::new()) - } else { - let derefs = cx.typeck_results() - .expr_adjustments(map_arg) - .iter() - .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) - .count(); - - ("", "*".repeat(derefs)) - }; - let sugg = format!( - "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", - snippet(cx, map_arg.span, ".."), - ); - span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); - } + let sugg = format!( + "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", + snippet(cx, map_arg.span, ".."), + ); + span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); + } } } diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 2244ebfb1292..c87f5daab6f2 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -6,7 +6,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; -use rustc_hir::{BindingAnnotation, Body, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; +use rustc_hir::{BindingAnnotation, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::sym; @@ -30,9 +30,9 @@ pub(super) fn check<'tcx>( if let Body {params: [p], value: body_expr, generator_kind: _ } = cx.tcx.hir().body(c.body); if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind; - let (replacement_kind, binded_ident) = match (&key_pat.kind, &val_pat.kind) { - (key, PatKind::Binding(_, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", value), - (PatKind::Binding(_, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", key), + let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) { + (key, PatKind::Binding(ann, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", ann, value), + (PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key), _ => return, }; @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( if_chain! { if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind; if let [local_ident] = path.segments; - if local_ident.ident.as_str() == binded_ident.as_str(); + if local_ident.ident.as_str() == bound_ident.as_str(); then { span_lint_and_sugg( @@ -60,13 +60,23 @@ pub(super) fn check<'tcx>( applicability, ); } else { + let ref_annotation = if annotation.0 == ByRef::Yes { + "ref " + } else { + "" + }; + let mut_annotation = if annotation.1 == Mutability::Mut { + "mut " + } else { + "" + }; span_lint_and_sugg( cx, ITER_KV_MAP, expr.span, &format!("iterating on a map's {replacement_kind}s"), "try", - format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{binded_ident}| {})", + format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})", snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)), applicability, ); diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index 15c1c618c513..fe88fa41fd91 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, print::with_forced_trimmed_paths}; use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; @@ -24,7 +24,9 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - cx, SUSPICIOUS_TO_OWNED, expr.span, - &format!("this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned"), + &with_forced_trimmed_paths!(format!( + "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" + )), "consider using, depending on intent", format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"), app, diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 09cb53331763..dc866ab6373b 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,6 +1,6 @@ //! Checks for uses of mutex where an atomic value could be used //! -//! This lint is **warn** by default +//! This lint is **allow** by default use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; @@ -20,6 +20,10 @@ declare_clippy_lint! { /// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and /// faster. /// + /// On the other hand, `Mutex`es are, in general, easier to + /// verify correctness. An atomic does not behave the same as + /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s commentary for more details. + /// /// ### Known problems /// This lint cannot detect if the mutex is actually used /// for waiting before a critical section. @@ -39,8 +43,8 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub MUTEX_ATOMIC, - nursery, - "using a mutex where an atomic value could be used instead" + restriction, + "using a mutex where an atomic value could be used instead." } declare_clippy_lint! { diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 4fbc8398e373..cff82b875f11 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -2,7 +2,7 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_utils::{ consts::{constant, constant_simple}, diagnostics::span_lint, - peel_hir_expr_refs, + peel_hir_expr_refs, peel_hir_expr_unary, }; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -98,8 +98,11 @@ impl ArithmeticSideEffects { } /// If `expr` is not a literal integer like `1`, returns `None`. + /// + /// Returns the absolute value of the expression, if this is an integer literal. fn literal_integer(expr: &hir::Expr<'_>) -> Option { - if let hir::ExprKind::Lit(ref lit) = expr.kind && let ast::LitKind::Int(n, _) = lit.node { + let actual = peel_hir_expr_unary(expr).0; + if let hir::ExprKind::Lit(ref lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node { Some(n) } else { @@ -123,12 +126,12 @@ impl ArithmeticSideEffects { if !matches!( op.node, hir::BinOpKind::Add - | hir::BinOpKind::Sub - | hir::BinOpKind::Mul | hir::BinOpKind::Div + | hir::BinOpKind::Mul | hir::BinOpKind::Rem | hir::BinOpKind::Shl | hir::BinOpKind::Shr + | hir::BinOpKind::Sub ) { return; }; diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 0a1b9d173cf9..fc655fe2d0bb 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -103,7 +103,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for range expressions `x..y` where both `x` and `y` - /// are constant and `x` is greater or equal to `y`. + /// are constant and `x` is greater to `y`. Also triggers if `x` is equal to `y` when they are conditions to a `for` loop. /// /// ### Why is this bad? /// Empty ranges yield no values so iterating them is a no-op. diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 0e7c5cca7240..c1677fb3da1c 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { // `res = clone(arg)` can be turned into `res = move arg;` // if `arg` is the only borrow of `cloned` at this point. - if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg], cloned, loc) { + if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) { continue; } @@ -178,7 +178,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { // StorageDead(pred_arg); // res = to_path_buf(cloned); // ``` - if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg, cloned], local, loc) { + if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) { continue; } diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 72c25592609b..9f487dedb8cb 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -9,6 +9,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::box_vec", "clippy::box_collection"), ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"), ("clippy::disallowed_method", "clippy::disallowed_methods"), ("clippy::disallowed_type", "clippy::disallowed_types"), ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index d4d506605206..bbbd9e4989e9 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -210,22 +210,25 @@ fn check_final_expr<'tcx>( // if desugar of `do yeet`, don't lint if let Some(inner_expr) = inner && let ExprKind::Call(path_expr, _) = inner_expr.kind - && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind { - return; + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind + { + return; } - if cx.tcx.hir().attrs(expr.hir_id).is_empty() { - let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); - if !borrows { - // check if expr return nothing - let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { - extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) - } else { - peeled_drop_expr.span - }; - - emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement); - } + if !cx.tcx.hir().attrs(expr.hir_id).is_empty() { + return; + } + let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); + if borrows { + return; } + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement); }, ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, semi_spans.clone()); @@ -292,7 +295,7 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { ControlFlow::Break(()) } else { - ControlFlow::Continue(Descend::from(!expr.span.from_expansion())) + ControlFlow::Continue(Descend::from(!e.span.from_expansion())) } }) .is_some() diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index c14f056a1f2d..229478b7ce3c 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -127,7 +127,7 @@ declare_clippy_lint! { /// `Vec` or a `VecDeque` (formerly called `RingBuf`). /// /// ### Why is this bad? - /// Gankro says: + /// Gankra says: /// /// > The TL;DR of `LinkedList` is that it's built on a massive amount of /// pointers and indirection. diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 42bccc7212b3..f864c520302e 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::visitors::is_local_used; use if_chain::if_chain; -use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind}; +use rustc_hir::{Body, Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -57,6 +59,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id; let parent_item = cx.tcx.hir().expect_item(parent); let assoc_item = cx.tcx.associated_item(impl_item.owner_id); + let contains_todo = |cx, body: &'_ Body<'_>| -> bool { + clippy_utils::visitors::for_each_expr(body.value, |e| { + if let Some(macro_call) = root_macro_call_first_node(cx, e) { + if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } else { + ControlFlow::Continue(()) + } + }) + .is_some() + }; if_chain! { if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind; if assoc_item.fn_has_self_parameter; @@ -65,6 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { let body = cx.tcx.hir().body(*body_id); if let [self_param, ..] = body.params; if !is_local_used(cx, body, self_param.pat.hir_id); + if !contains_todo(cx, body); then { span_lint_and_help( cx, @@ -72,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { self_param.span, "unused `self` argument", None, - "consider refactoring to a associated function", + "consider refactoring to an associated function", ); } } diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c86f24cbd378..c4d8c28f0606 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -1058,7 +1058,7 @@ fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) - fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { let map = cx.tcx.hir(); - match map.find_parent((hir_id)) { + match map.find_parent(hir_id) { Some(hir::Node::Local(local)) => Some(local), Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id), _ => None, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8290fe9ecb4c..7a4a9036dd36 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -22,6 +22,9 @@ extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; extern crate rustc_data_structures; +// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate. +#[allow(unused_extern_crates)] +extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_typeck; @@ -116,6 +119,8 @@ use crate::consts::{constant, Constant}; use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param}; use crate::visitors::for_each_expr; +use rustc_middle::hir::nested_filter; + #[macro_export] macro_rules! extract_msrv_attr { ($context:ident) => { @@ -1253,22 +1258,33 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } } -pub struct ContainsName { +pub struct ContainsName<'a, 'tcx> { + pub cx: &'a LateContext<'tcx>, pub name: Symbol, pub result: bool, } -impl<'tcx> Visitor<'tcx> for ContainsName { +impl<'a, 'tcx> Visitor<'tcx> for ContainsName<'a, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + fn visit_name(&mut self, name: Symbol) { if self.name == name { self.result = true; } } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } } /// Checks if an `Expr` contains a certain name. -pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { - let mut cn = ContainsName { name, result: false }; +pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool { + let mut cn = ContainsName { + name, + result: false, + cx, + }; cn.visit_expr(expr); cn.result } @@ -1304,6 +1320,7 @@ pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) } } +/// Gets the enclosing block, if any. pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> { let map = &cx.tcx.hir(); let enclosing_node = map @@ -2244,6 +2261,18 @@ pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<' (e, count - remaining) } +/// Peels off all unary operators of an expression. Returns the underlying expression and the number +/// of operators removed. +pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { + let mut count: usize = 0; + let mut curr_expr = expr; + while let ExprKind::Unary(_, local_expr) = curr_expr.kind { + count = count.wrapping_add(1); + curr_expr = local_expr; + } + (curr_expr, count) +} + /// Peels off all references on the expression. Returns the underlying expression and the number of /// references removed. pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 395d46e7a2f8..9adae7733894 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -1,16 +1,11 @@ -use super::possible_origin::PossibleOriginVisitor; +use super::{possible_origin::PossibleOriginVisitor, transitive_relation::TransitiveRelation}; use crate::ty::is_copy; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::FxHashMap; use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::LateContext; -use rustc_middle::mir::{ - self, visit::Visitor as _, BasicBlock, Local, Location, Mutability, Statement, StatementKind, Terminator, -}; -use rustc_middle::ty::{self, visit::TypeVisitor, TyCtxt}; -use rustc_mir_dataflow::{ - fmt::DebugWithContext, impls::MaybeStorageLive, lattice::JoinSemiLattice, Analysis, AnalysisDomain, - CallReturnPlaces, ResultsCursor, -}; +use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; +use rustc_middle::ty::{self, visit::TypeVisitor}; +use rustc_mir_dataflow::{impls::MaybeStorageLive, Analysis, ResultsCursor}; use std::borrow::Cow; use std::ops::ControlFlow; @@ -18,120 +13,78 @@ use std::ops::ControlFlow; /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// possible borrowers of `a`. #[allow(clippy::module_name_repetitions)] -struct PossibleBorrowerAnalysis<'b, 'tcx> { - tcx: TyCtxt<'tcx>, +struct PossibleBorrowerVisitor<'a, 'b, 'tcx> { + possible_borrower: TransitiveRelation, body: &'b mir::Body<'tcx>, + cx: &'a LateContext<'tcx>, possible_origin: FxHashMap>, } -#[derive(Clone, Debug, Eq, PartialEq)] -struct PossibleBorrowerState { - map: FxIndexMap>, - domain_size: usize, -} - -impl PossibleBorrowerState { - fn new(domain_size: usize) -> Self { +impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn new( + cx: &'a LateContext<'tcx>, + body: &'b mir::Body<'tcx>, + possible_origin: FxHashMap>, + ) -> Self { Self { - map: FxIndexMap::default(), - domain_size, + possible_borrower: TransitiveRelation::default(), + cx, + body, + possible_origin, } } - #[allow(clippy::similar_names)] - fn add(&mut self, borrowed: Local, borrower: Local) { - self.map - .entry(borrowed) - .or_insert(BitSet::new_empty(self.domain_size)) - .insert(borrower); - } -} - -impl DebugWithContext for PossibleBorrowerState { - fn fmt_with(&self, _ctxt: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - <_ as std::fmt::Debug>::fmt(self, f) - } - fn fmt_diff_with(&self, _old: &Self, _ctxt: &C, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - unimplemented!() - } -} + fn into_map( + self, + cx: &'a LateContext<'tcx>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, + ) -> PossibleBorrowerMap<'b, 'tcx> { + let mut map = FxHashMap::default(); + for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { + if is_copy(cx, self.body.local_decls[row].ty) { + continue; + } -impl JoinSemiLattice for PossibleBorrowerState { - fn join(&mut self, other: &Self) -> bool { - let mut changed = false; - for (&borrowed, borrowers) in other.map.iter() { + let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len()); + borrowers.remove(mir::Local::from_usize(0)); if !borrowers.is_empty() { - changed |= self - .map - .entry(borrowed) - .or_insert(BitSet::new_empty(self.domain_size)) - .union(borrowers); + map.insert(row, borrowers); } } - changed - } -} - -impl<'b, 'tcx> AnalysisDomain<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { - type Domain = PossibleBorrowerState; - - const NAME: &'static str = "possible_borrower"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - PossibleBorrowerState::new(body.local_decls.len()) - } - - fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _entry_set: &mut Self::Domain) {} -} -impl<'b, 'tcx> PossibleBorrowerAnalysis<'b, 'tcx> { - fn new( - tcx: TyCtxt<'tcx>, - body: &'b mir::Body<'tcx>, - possible_origin: FxHashMap>, - ) -> Self { - Self { - tcx, - body, - possible_origin, + let bs = BitSet::new_empty(self.body.local_decls.len()); + PossibleBorrowerMap { + map, + maybe_live, + bitset: (bs.clone(), bs), } } } -impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { - fn apply_call_return_effect( - &self, - _state: &mut Self::Domain, - _block: BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - } - - fn apply_statement_effect(&self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location) { - if let StatementKind::Assign(box (place, rvalue)) = &statement.kind { - let lhs = place.local; - match rvalue { - mir::Rvalue::Ref(_, _, borrowed) => { - state.add(borrowed.local, lhs); - }, - other => { - if ContainsRegion - .visit_ty(place.ty(&self.body.local_decls, self.tcx).ty) - .is_continue() - { - return; +impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> { + fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) { + let lhs = place.local; + match rvalue { + mir::Rvalue::Ref(_, _, borrowed) => { + self.possible_borrower.add(borrowed.local, lhs); + }, + other => { + if ContainsRegion + .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty) + .is_continue() + { + return; + } + rvalue_locals(other, |rhs| { + if lhs != rhs { + self.possible_borrower.add(rhs, lhs); } - rvalue_locals(other, |rhs| { - if lhs != rhs { - state.add(rhs, lhs); - } - }); - }, - } + }); + }, } } - fn apply_terminator_effect(&self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location) { + fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) { if let mir::TerminatorKind::Call { args, destination: mir::Place { local: dest, .. }, @@ -171,10 +124,10 @@ impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> { for y in mutable_variables { for x in &immutable_borrowers { - state.add(*x, y); + self.possible_borrower.add(*x, y); } for x in &mutable_borrowers { - state.add(*x, y); + self.possible_borrower.add(*x, y); } } } @@ -210,98 +163,73 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { } } -/// Result of `PossibleBorrowerAnalysis`. +/// Result of `PossibleBorrowerVisitor`. #[allow(clippy::module_name_repetitions)] pub struct PossibleBorrowerMap<'b, 'tcx> { - body: &'b mir::Body<'tcx>, - possible_borrower: ResultsCursor<'b, 'tcx, PossibleBorrowerAnalysis<'b, 'tcx>>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'b>>, - pushed: BitSet, - stack: Vec, + /// Mapping `Local -> its possible borrowers` + pub map: FxHashMap>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, + // Caches to avoid allocation of `BitSet` on every query + pub bitset: (BitSet, BitSet), } -impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { - pub fn new(cx: &LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { +impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { + pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self { let possible_origin = { let mut vis = PossibleOriginVisitor::new(mir); vis.visit_body(mir); vis.into_map(cx) }; - let possible_borrower = PossibleBorrowerAnalysis::new(cx.tcx, mir, possible_origin) + let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) .into_engine(cx.tcx, mir) - .pass_name("possible_borrower") + .pass_name("redundant_clone") .iterate_to_fixpoint() .into_results_cursor(mir); - let maybe_live = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) - .into_engine(cx.tcx, mir) - .pass_name("possible_borrower") - .iterate_to_fixpoint() - .into_results_cursor(mir); - PossibleBorrowerMap { - body: mir, - possible_borrower, - maybe_live, - pushed: BitSet::new_empty(mir.local_decls.len()), - stack: Vec::with_capacity(mir.local_decls.len()), - } + let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); + vis.visit_body(mir); + vis.into_map(cx, maybe_storage_live_result) } - /// Returns true if the set of borrowers of `borrowed` living at `at` includes no more than - /// `borrowers`. - /// Notes: - /// 1. It would be nice if `PossibleBorrowerMap` could store `cx` so that `at_most_borrowers` - /// would not require it to be passed in. But a `PossibleBorrowerMap` is stored in `LintPass` - /// `Dereferencing`, which outlives any `LateContext`. - /// 2. In all current uses of `at_most_borrowers`, `borrowers` is a slice of at most two - /// elements. Thus, `borrowers.contains(...)` is effectively a constant-time operation. If - /// `at_most_borrowers`'s uses were to expand beyond this, its implementation might have to be - /// adjusted. - pub fn at_most_borrowers( + /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`. + pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { + self.bounded_borrowers(borrowers, borrowers, borrowed, at) + } + + /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below` + /// but no more than `above`. + pub fn bounded_borrowers( &mut self, - cx: &LateContext<'tcx>, - borrowers: &[mir::Local], + below: &[mir::Local], + above: &[mir::Local], borrowed: mir::Local, at: mir::Location, ) -> bool { - if is_copy(cx, self.body.local_decls[borrowed].ty) { - return true; - } - - self.possible_borrower.seek_before_primary_effect(at); - self.maybe_live.seek_before_primary_effect(at); - - let possible_borrower = &self.possible_borrower.get().map; - let maybe_live = &self.maybe_live; - - self.pushed.clear(); - self.stack.clear(); + self.maybe_live.seek_after_primary_effect(at); - if let Some(borrowers) = possible_borrower.get(&borrowed) { - for b in borrowers.iter() { - if self.pushed.insert(b) { - self.stack.push(b); - } + self.bitset.0.clear(); + let maybe_live = &mut self.maybe_live; + if let Some(bitset) = self.map.get(&borrowed) { + for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) { + self.bitset.0.insert(b); } } else { - // Nothing borrows `borrowed` at `at`. - return true; + return false; } - while let Some(borrower) = self.stack.pop() { - if maybe_live.contains(borrower) && !borrowers.contains(&borrower) { - return false; - } + self.bitset.1.clear(); + for b in below { + self.bitset.1.insert(*b); + } - if let Some(borrowers) = possible_borrower.get(&borrower) { - for b in borrowers.iter() { - if self.pushed.insert(b) { - self.stack.push(b); - } - } - } + if !self.bitset.0.superset(&self.bitset.1) { + return false; + } + + for b in above { + self.bitset.0.remove(*b); } - true + self.bitset.0.is_empty() } pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index ba5bc9c3135d..dbf9f3b621d7 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -20,8 +20,9 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,65,0 { LET_ELSE } - 1,62,0 { BOOL_THEN_SOME } + 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,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 } @@ -45,7 +46,6 @@ msrv_aliases! { 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } - 1,55,0 { SEEK_REWIND } } fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 9ca50105ae57..95eebab75677 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -47,7 +47,6 @@ pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; #[cfg(feature = "internal")] pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; -pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 863fb60fcfca..14c01a60b4c3 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -724,3 +724,14 @@ pub fn for_each_local_assignment<'tcx, B>( ControlFlow::Continue(()) } } + +pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool { + for_each_expr(expr, |e| { + if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} diff --git a/rust-toolchain b/rust-toolchain index 9399d422036d..40a6f47095ec 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-12-29" +channel = "nightly-2023-01-12" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/driver.rs b/src/driver.rs index bcc096c570e1..d521e8d88398 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -256,11 +256,14 @@ pub fn main() { LazyLock::force(&ICE_HOOK); exit(rustc_driver::catch_with_exit_code(move || { let mut orig_args: Vec = env::args().collect(); + let has_sysroot_arg = arg_value(&orig_args, "--sysroot", |_| true).is_some(); let sys_root_env = std::env::var("SYSROOT").ok(); let pass_sysroot_env_if_given = |args: &mut Vec, sys_root_env| { if let Some(sys_root) = sys_root_env { - args.extend(vec!["--sysroot".into(), sys_root]); + if !has_sysroot_arg { + args.extend(vec!["--sysroot".into(), sys_root]); + } }; }; diff --git a/tests/integration.rs b/tests/integration.rs index 818ff70b33f4..a771d8b87c81 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,3 +1,12 @@ +//! This test is meant to only be run in CI. To run it locally use: +//! +//! `env INTEGRATION=rust-lang/log cargo test --test integration --features=integration` +//! +//! You can use a different `INTEGRATION` value to test different repositories. +//! +//! This test will clone the specified repository and run Clippy on it. The test succeeds, if +//! Clippy doesn't produce an ICE. Lint warnings are ignored by this test. + #![cfg(feature = "integration")] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs index 36db9e54a228..fb5b1b193f84 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs @@ -107,7 +107,7 @@ fn rhs_is_different() { fn unary() { // is explicitly on the list let _ = -OutOfNames; - // is specifically on the list + // is explicitly on the list let _ = -Foo; // not on the list let _ = -Bar; diff --git a/tests/ui-toml/dbg_macro/dbg_macro.stderr b/tests/ui-toml/dbg_macro/dbg_macro.stderr index 46efb86dcfc5..859383a71194 100644 --- a/tests/ui-toml/dbg_macro/dbg_macro.stderr +++ b/tests/ui-toml/dbg_macro/dbg_macro.stderr @@ -1,99 +1,99 @@ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:5:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:9:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if n <= 1 { | ~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:10:9 | LL | dbg!(1) | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1 | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:12:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | n * factorial(n - 1) | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:17:5 | LL | dbg!(42); | ^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 42; | ~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:18:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | dbg!(dbg!(42)); | ~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:19:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:20:5 | LL | dbg!(1, 2, dbg!(3, 4)); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, dbg!(3, 4)); | ~~~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:21:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index b5ed8988a518..918cf81c600a 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -2,6 +2,7 @@ clippy::assign_op_pattern, clippy::erasing_op, clippy::identity_op, + clippy::no_effect, clippy::op_ref, clippy::unnecessary_owned_empty_strings, arithmetic_overflow, @@ -12,31 +13,95 @@ use core::num::{Saturating, Wrapping}; +#[derive(Clone, Copy)] pub struct Custom; macro_rules! impl_arith { - ( $( $_trait:ident, $ty:ty, $method:ident; )* ) => { + ( $( $_trait:ident, $lhs:ty, $rhs:ty, $method:ident; )* ) => { $( - impl core::ops::$_trait<$ty> for Custom { - type Output = Self; - fn $method(self, _: $ty) -> Self::Output { Self } + impl core::ops::$_trait<$lhs> for $rhs { + type Output = Custom; + fn $method(self, _: $lhs) -> Self::Output { todo!() } + } + )* + } +} + +macro_rules! impl_assign_arith { + ( $( $_trait:ident, $lhs:ty, $rhs:ty, $method:ident; )* ) => { + $( + impl core::ops::$_trait<$lhs> for $rhs { + fn $method(&mut self, _: $lhs) {} } )* } } impl_arith!( - Add, i32, add; - Div, i32, div; - Mul, i32, mul; - Sub, i32, sub; - - Add, f64, add; - Div, f64, div; - Mul, f64, mul; - Sub, f64, sub; + Add, Custom, Custom, add; + Div, Custom, Custom, div; + Mul, Custom, Custom, mul; + Rem, Custom, Custom, rem; + Sub, Custom, Custom, sub; + + Add, Custom, &Custom, add; + Div, Custom, &Custom, div; + Mul, Custom, &Custom, mul; + Rem, Custom, &Custom, rem; + Sub, Custom, &Custom, sub; + + Add, &Custom, Custom, add; + Div, &Custom, Custom, div; + Mul, &Custom, Custom, mul; + Rem, &Custom, Custom, rem; + Sub, &Custom, Custom, sub; + + Add, &Custom, &Custom, add; + Div, &Custom, &Custom, div; + Mul, &Custom, &Custom, mul; + Rem, &Custom, &Custom, rem; + Sub, &Custom, &Custom, sub; +); + +impl_assign_arith!( + AddAssign, Custom, Custom, add_assign; + DivAssign, Custom, Custom, div_assign; + MulAssign, Custom, Custom, mul_assign; + RemAssign, Custom, Custom, rem_assign; + SubAssign, Custom, Custom, sub_assign; + + AddAssign, Custom, &Custom, add_assign; + DivAssign, Custom, &Custom, div_assign; + MulAssign, Custom, &Custom, mul_assign; + RemAssign, Custom, &Custom, rem_assign; + SubAssign, Custom, &Custom, sub_assign; + + AddAssign, &Custom, Custom, add_assign; + DivAssign, &Custom, Custom, div_assign; + MulAssign, &Custom, Custom, mul_assign; + RemAssign, &Custom, Custom, rem_assign; + SubAssign, &Custom, Custom, sub_assign; + + AddAssign, &Custom, &Custom, add_assign; + DivAssign, &Custom, &Custom, div_assign; + MulAssign, &Custom, &Custom, mul_assign; + RemAssign, &Custom, &Custom, rem_assign; + SubAssign, &Custom, &Custom, sub_assign; ); +impl core::ops::Neg for Custom { + type Output = Custom; + fn neg(self) -> Self::Output { + todo!() + } +} +impl core::ops::Neg for &Custom { + type Output = Custom; + fn neg(self) -> Self::Output { + todo!() + } +} + pub fn association_with_structures_should_not_trigger_the_lint() { enum Foo { Bar = -2, @@ -125,6 +190,18 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n *= &0; _n *= 1; _n *= &1; + _n += -0; + _n += &-0; + _n -= -0; + _n -= &-0; + _n /= -99; + _n /= &-99; + _n %= -99; + _n %= &-99; + _n *= -0; + _n *= &-0; + _n *= -1; + _n *= &-1; // Binary _n = _n + 0; @@ -158,8 +235,9 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n = -&i32::MIN; } -pub fn runtime_ops() { +pub fn unknown_ops_or_runtime_ops_that_can_overflow() { let mut _n = i32::MAX; + let mut _custom = Custom; // Assign _n += 1; @@ -172,6 +250,36 @@ pub fn runtime_ops() { _n %= &0; _n *= 2; _n *= &2; + _n += -1; + _n += &-1; + _n -= -1; + _n -= &-1; + _n /= -0; + _n /= &-0; + _n %= -0; + _n %= &-0; + _n *= -2; + _n *= &-2; + _custom += Custom; + _custom += &Custom; + _custom -= Custom; + _custom -= &Custom; + _custom /= Custom; + _custom /= &Custom; + _custom %= Custom; + _custom %= &Custom; + _custom *= Custom; + _custom *= &Custom; + _custom += -Custom; + _custom += &-Custom; + _custom -= -Custom; + _custom -= &-Custom; + _custom /= -Custom; + _custom /= &-Custom; + _custom %= -Custom; + _custom %= &-Custom; + _custom *= -Custom; + _custom *= &-Custom; // Binary _n = _n + 1; @@ -193,36 +301,73 @@ pub fn runtime_ops() { _n = 23 + &85; _n = &23 + 85; _n = &23 + &85; - - // Custom - let _ = Custom + 0; - let _ = Custom + 1; - let _ = Custom + 2; - let _ = Custom + 0.0; - let _ = Custom + 1.0; - let _ = Custom + 2.0; - let _ = Custom - 0; - let _ = Custom - 1; - let _ = Custom - 2; - let _ = Custom - 0.0; - let _ = Custom - 1.0; - let _ = Custom - 2.0; - let _ = Custom / 0; - let _ = Custom / 1; - let _ = Custom / 2; - let _ = Custom / 0.0; - let _ = Custom / 1.0; - let _ = Custom / 2.0; - let _ = Custom * 0; - let _ = Custom * 1; - let _ = Custom * 2; - let _ = Custom * 0.0; - let _ = Custom * 1.0; - let _ = Custom * 2.0; + _custom = _custom + _custom; + _custom = _custom + &_custom; + _custom = Custom + _custom; + _custom = &Custom + _custom; + _custom = _custom - Custom; + _custom = _custom - &Custom; + _custom = Custom - _custom; + _custom = &Custom - _custom; + _custom = _custom / Custom; + _custom = _custom / &Custom; + _custom = _custom % Custom; + _custom = _custom % &Custom; + _custom = _custom * Custom; + _custom = _custom * &Custom; + _custom = Custom * _custom; + _custom = &Custom * _custom; + _custom = Custom + &Custom; + _custom = &Custom + Custom; + _custom = &Custom + &Custom; // Unary _n = -_n; _n = -&_n; + _custom = -_custom; + _custom = -&_custom; +} + +// Copied and pasted from the `integer_arithmetic` lint for comparison. +pub fn integer_arithmetic() { + let mut i = 1i32; + let mut var1 = 0i32; + let mut var2 = -1i32; + + 1 + i; + i * 2; + 1 % i / 2; + i - 2 + 2 - i; + -i; + i >> 1; + i << 1; + + -1; + -(-1); + + i & 1; + i | 1; + i ^ 1; + + i += 1; + i -= 1; + i *= 2; + i /= 2; + i /= 0; + i /= -1; + i /= var1; + i /= var2; + i %= 2; + i %= 0; + i %= -1; + i %= var1; + i %= var2; + i <<= 3; + i >>= 2; + + i |= 1; + i &= 1; + i ^= i; } fn main() {} diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 9fe4b7cf28d8..5e349f6b497c 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,5 +1,5 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:165:5 + --> $DIR/arithmetic_side_effects.rs:243:5 | LL | _n += 1; | ^^^^^^^ @@ -7,328 +7,592 @@ LL | _n += 1; = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:166:5 + --> $DIR/arithmetic_side_effects.rs:244:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:167:5 + --> $DIR/arithmetic_side_effects.rs:245:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:168:5 + --> $DIR/arithmetic_side_effects.rs:246:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:169:5 + --> $DIR/arithmetic_side_effects.rs:247:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:170:5 + --> $DIR/arithmetic_side_effects.rs:248:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:171:5 + --> $DIR/arithmetic_side_effects.rs:249:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:172:5 + --> $DIR/arithmetic_side_effects.rs:250:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:173:5 + --> $DIR/arithmetic_side_effects.rs:251:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:174:5 + --> $DIR/arithmetic_side_effects.rs:252:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:177:10 + --> $DIR/arithmetic_side_effects.rs:253:5 + | +LL | _n += -1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:254:5 + | +LL | _n += &-1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:255:5 + | +LL | _n -= -1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:256:5 + | +LL | _n -= &-1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:257:5 + | +LL | _n /= -0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:258:5 + | +LL | _n /= &-0; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:259:5 + | +LL | _n %= -0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:260:5 + | +LL | _n %= &-0; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:261:5 + | +LL | _n *= -2; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:262:5 + | +LL | _n *= &-2; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:263:5 + | +LL | _custom += Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:264:5 + | +LL | _custom += &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:265:5 + | +LL | _custom -= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:266:5 + | +LL | _custom -= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:267:5 + | +LL | _custom /= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:268:5 + | +LL | _custom /= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:269:5 + | +LL | _custom %= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:270:5 + | +LL | _custom %= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:271:5 + | +LL | _custom *= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:272:5 + | +LL | _custom *= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:273:5 + | +LL | _custom += -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:274:5 + | +LL | _custom += &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:275:5 + | +LL | _custom -= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:276:5 + | +LL | _custom -= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:277:5 + | +LL | _custom /= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:278:5 + | +LL | _custom /= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:279:5 + | +LL | _custom %= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:280:5 + | +LL | _custom %= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:281:5 + | +LL | _custom *= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:282:5 + | +LL | _custom *= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:285:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:178:10 + --> $DIR/arithmetic_side_effects.rs:286:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:179:10 + --> $DIR/arithmetic_side_effects.rs:287:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:180:10 + --> $DIR/arithmetic_side_effects.rs:288:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:181:10 + --> $DIR/arithmetic_side_effects.rs:289:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:182:10 + --> $DIR/arithmetic_side_effects.rs:290:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:183:10 + --> $DIR/arithmetic_side_effects.rs:291:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:184:10 + --> $DIR/arithmetic_side_effects.rs:292:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:185:10 + --> $DIR/arithmetic_side_effects.rs:293:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:186:10 + --> $DIR/arithmetic_side_effects.rs:294:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:187:10 + --> $DIR/arithmetic_side_effects.rs:295:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:188:10 + --> $DIR/arithmetic_side_effects.rs:296:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:189:10 + --> $DIR/arithmetic_side_effects.rs:297:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:190:10 + --> $DIR/arithmetic_side_effects.rs:298:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:191:10 + --> $DIR/arithmetic_side_effects.rs:299:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:192:10 + --> $DIR/arithmetic_side_effects.rs:300:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:193:10 + --> $DIR/arithmetic_side_effects.rs:301:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:194:10 + --> $DIR/arithmetic_side_effects.rs:302:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:195:10 + --> $DIR/arithmetic_side_effects.rs:303:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:198:13 + --> $DIR/arithmetic_side_effects.rs:304:15 | -LL | let _ = Custom + 0; - | ^^^^^^^^^^ +LL | _custom = _custom + _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:199:13 + --> $DIR/arithmetic_side_effects.rs:305:15 | -LL | let _ = Custom + 1; - | ^^^^^^^^^^ +LL | _custom = _custom + &_custom; + | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:200:13 + --> $DIR/arithmetic_side_effects.rs:306:15 | -LL | let _ = Custom + 2; - | ^^^^^^^^^^ +LL | _custom = Custom + _custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:201:13 + --> $DIR/arithmetic_side_effects.rs:307:15 | -LL | let _ = Custom + 0.0; - | ^^^^^^^^^^^^ +LL | _custom = &Custom + _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:202:13 + --> $DIR/arithmetic_side_effects.rs:308:15 | -LL | let _ = Custom + 1.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom - Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:203:13 + --> $DIR/arithmetic_side_effects.rs:309:15 | -LL | let _ = Custom + 2.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom - &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:204:13 + --> $DIR/arithmetic_side_effects.rs:310:15 | -LL | let _ = Custom - 0; - | ^^^^^^^^^^ +LL | _custom = Custom - _custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:205:13 + --> $DIR/arithmetic_side_effects.rs:311:15 | -LL | let _ = Custom - 1; - | ^^^^^^^^^^ +LL | _custom = &Custom - _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:206:13 + --> $DIR/arithmetic_side_effects.rs:312:15 | -LL | let _ = Custom - 2; - | ^^^^^^^^^^ +LL | _custom = _custom / Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:207:13 + --> $DIR/arithmetic_side_effects.rs:313:15 | -LL | let _ = Custom - 0.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom / &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:208:13 + --> $DIR/arithmetic_side_effects.rs:314:15 | -LL | let _ = Custom - 1.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom % Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:209:13 + --> $DIR/arithmetic_side_effects.rs:315:15 | -LL | let _ = Custom - 2.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom % &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:210:13 + --> $DIR/arithmetic_side_effects.rs:316:15 | -LL | let _ = Custom / 0; - | ^^^^^^^^^^ +LL | _custom = _custom * Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:211:13 + --> $DIR/arithmetic_side_effects.rs:317:15 | -LL | let _ = Custom / 1; - | ^^^^^^^^^^ +LL | _custom = _custom * &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:212:13 + --> $DIR/arithmetic_side_effects.rs:318:15 | -LL | let _ = Custom / 2; - | ^^^^^^^^^^ +LL | _custom = Custom * _custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:213:13 + --> $DIR/arithmetic_side_effects.rs:319:15 | -LL | let _ = Custom / 0.0; - | ^^^^^^^^^^^^ +LL | _custom = &Custom * _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:214:13 + --> $DIR/arithmetic_side_effects.rs:320:15 | -LL | let _ = Custom / 1.0; - | ^^^^^^^^^^^^ +LL | _custom = Custom + &Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:215:13 + --> $DIR/arithmetic_side_effects.rs:321:15 | -LL | let _ = Custom / 2.0; - | ^^^^^^^^^^^^ +LL | _custom = &Custom + Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:216:13 + --> $DIR/arithmetic_side_effects.rs:322:15 | -LL | let _ = Custom * 0; - | ^^^^^^^^^^ +LL | _custom = &Custom + &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:217:13 + --> $DIR/arithmetic_side_effects.rs:325:10 | -LL | let _ = Custom * 1; - | ^^^^^^^^^^ +LL | _n = -_n; + | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:218:13 + --> $DIR/arithmetic_side_effects.rs:326:10 | -LL | let _ = Custom * 2; - | ^^^^^^^^^^ +LL | _n = -&_n; + | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:219:13 + --> $DIR/arithmetic_side_effects.rs:327:15 | -LL | let _ = Custom * 0.0; - | ^^^^^^^^^^^^ +LL | _custom = -_custom; + | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:220:13 + --> $DIR/arithmetic_side_effects.rs:328:15 | -LL | let _ = Custom * 1.0; - | ^^^^^^^^^^^^ +LL | _custom = -&_custom; + | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:221:13 + --> $DIR/arithmetic_side_effects.rs:337:5 | -LL | let _ = Custom * 2.0; - | ^^^^^^^^^^^^ +LL | 1 + i; + | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:224:10 + --> $DIR/arithmetic_side_effects.rs:338:5 | -LL | _n = -_n; - | ^^^ +LL | i * 2; + | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:225:10 + --> $DIR/arithmetic_side_effects.rs:340:5 | -LL | _n = -&_n; - | ^^^^ +LL | i - 2 + 2 - i; + | ^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:341:5 + | +LL | -i; + | ^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:342:5 + | +LL | i >> 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:343:5 + | +LL | i << 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:352:5 + | +LL | i += 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:353:5 + | +LL | i -= 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:354:5 + | +LL | i *= 2; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:356:5 + | +LL | i /= 0; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:358:5 + | +LL | i /= var1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:359:5 + | +LL | i /= var2; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:361:5 + | +LL | i %= 0; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:363:5 + | +LL | i %= var1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:364:5 + | +LL | i %= var2; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:365:5 + | +LL | i <<= 3; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:366:5 + | +LL | i >>= 2; + | ^^^^^^^ -error: aborting due to 55 previous errors +error: aborting due to 99 previous errors diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed index 911fa856aa0a..7e9f074fdcab 100644 --- a/tests/ui/box_default.fixed +++ b/tests/ui/box_default.fixed @@ -21,16 +21,16 @@ macro_rules! outer { fn main() { let _string: Box = Box::default(); let _byte = Box::::default(); - let _vec = Box::>::default(); + let _vec = Box::>::default(); let _impl = Box::::default(); let _impl2 = Box::::default(); let _impl3: Box = Box::default(); let _own = Box::new(OwnDefault::default()); // should not lint - let _in_macro = outer!(Box::::default()); - let _string_default = outer!(Box::::default()); + let _in_macro = outer!(Box::::default()); + let _string_default = outer!(Box::::default()); let _vec2: Box> = Box::default(); let _vec3: Box> = Box::default(); - let _vec4: Box<_> = Box::>::default(); + let _vec4: Box<_> = Box::>::default(); let _more = ret_ty_fn(); call_ty_fn(Box::default()); } @@ -54,4 +54,14 @@ impl Read for ImplementsDefault { fn issue_9621_dyn_trait() { let _: Box = Box::::default(); + issue_10089(); +} + +fn issue_10089() { + let _closure = || { + #[derive(Default)] + struct WeirdPathed; + + let _ = Box::::default(); + }; } diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index 20019c2ee5a0..5c8d0b8354cc 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -54,4 +54,14 @@ impl Read for ImplementsDefault { fn issue_9621_dyn_trait() { let _: Box = Box::new(ImplementsDefault::default()); + issue_10089(); +} + +fn issue_10089() { + let _closure = || { + #[derive(Default)] + struct WeirdPathed; + + let _ = Box::new(WeirdPathed::default()); + }; } diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index 5ea410331afb..249eb340f96c 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -16,7 +16,7 @@ error: `Box::new(_)` of default value --> $DIR/box_default.rs:24:16 | LL | let _vec = Box::new(Vec::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:25:17 @@ -40,13 +40,13 @@ error: `Box::new(_)` of default value --> $DIR/box_default.rs:29:28 | LL | let _in_macro = outer!(Box::new(String::new())); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:30:34 | LL | let _string_default = outer!(Box::new(String::from(""))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:31:46 @@ -64,7 +64,7 @@ error: `Box::new(_)` of default value --> $DIR/box_default.rs:33:25 | LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0])); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:35:16 @@ -84,5 +84,11 @@ error: `Box::new(_)` of default value LL | let _: Box = Box::new(ImplementsDefault::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` -error: aborting due to 14 previous errors +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:65:17 + | +LL | let _ = Box::new(WeirdPathed::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: aborting due to 15 previous errors diff --git a/tests/ui/case_sensitive_file_extension_comparisons.fixed b/tests/ui/case_sensitive_file_extension_comparisons.fixed new file mode 100644 index 000000000000..5fbaa64db39e --- /dev/null +++ b/tests/ui/case_sensitive_file_extension_comparisons.fixed @@ -0,0 +1,67 @@ +// run-rustfix +#![warn(clippy::case_sensitive_file_extension_comparisons)] + +use std::string::String; + +struct TestStruct; + +impl TestStruct { + fn ends_with(self, _arg: &str) {} +} + +#[allow(dead_code)] +fn is_rust_file(filename: &str) -> bool { + std::path::Path::new(filename) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) +} + +fn main() { + // std::string::String and &str should trigger the lint failure with .ext12 + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + let _ = std::path::Path::new("str") + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + + // The fixup should preserve the indentation level + { + let _ = std::path::Path::new("str") + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + } + + // The test struct should not trigger the lint failure with .ext12 + TestStruct {}.ends_with(".ext12"); + + // std::string::String and &str should trigger the lint failure with .EXT12 + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + let _ = std::path::Path::new("str") + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + + // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase + let _ = String::new().to_lowercase().ends_with(".EXT12"); + let _ = String::new().to_uppercase().ends_with(".EXT12"); + + // The test struct should not trigger the lint failure with .EXT12 + TestStruct {}.ends_with(".EXT12"); + + // Should not trigger the lint failure with .eXT12 + let _ = String::new().ends_with(".eXT12"); + let _ = "str".ends_with(".eXT12"); + TestStruct {}.ends_with(".eXT12"); + + // Should not trigger the lint failure with .EXT123 (too long) + let _ = String::new().ends_with(".EXT123"); + let _ = "str".ends_with(".EXT123"); + TestStruct {}.ends_with(".EXT123"); + + // Shouldn't fail if it doesn't start with a dot + let _ = String::new().ends_with("a.ext"); + let _ = "str".ends_with("a.extA"); + TestStruct {}.ends_with("a.ext"); +} diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs index 6f0485b5279b..3c0d4821f9f3 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::case_sensitive_file_extension_comparisons)] use std::string::String; @@ -5,9 +6,10 @@ use std::string::String; struct TestStruct; impl TestStruct { - fn ends_with(self, arg: &str) {} + fn ends_with(self, _arg: &str) {} } +#[allow(dead_code)] fn is_rust_file(filename: &str) -> bool { filename.ends_with(".rs") } @@ -17,6 +19,11 @@ fn main() { let _ = String::new().ends_with(".ext12"); let _ = "str".ends_with(".ext12"); + // The fixup should preserve the indentation level + { + let _ = "str".ends_with(".ext12"); + } + // The test struct should not trigger the lint failure with .ext12 TestStruct {}.ends_with(".ext12"); @@ -24,6 +31,10 @@ fn main() { let _ = String::new().ends_with(".EXT12"); let _ = "str".ends_with(".EXT12"); + // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase + let _ = String::new().to_lowercase().ends_with(".EXT12"); + let _ = String::new().to_uppercase().ends_with(".EXT12"); + // The test struct should not trigger the lint failure with .EXT12 TestStruct {}.ends_with(".EXT12"); diff --git a/tests/ui/case_sensitive_file_extension_comparisons.stderr b/tests/ui/case_sensitive_file_extension_comparisons.stderr index a28dd8bd5ad3..44c8e3fdf740 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.stderr +++ b/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -1,43 +1,87 @@ error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:12:14 + --> $DIR/case_sensitive_file_extension_comparisons.rs:14:5 | LL | filename.ends_with(".rs") - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead = note: `-D clippy::case-sensitive-file-extension-comparisons` implied by `-D warnings` +help: use std::path::Path + | +LL ~ std::path::Path::new(filename) +LL + .extension() +LL + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:17:27 + --> $DIR/case_sensitive_file_extension_comparisons.rs:19:13 | LL | let _ = String::new().ends_with(".ext12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:18:19 + --> $DIR/case_sensitive_file_extension_comparisons.rs:20:13 | LL | let _ = "str".ends_with(".ext12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new("str") +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:24:27 + --> $DIR/case_sensitive_file_extension_comparisons.rs:24:17 + | +LL | let _ = "str".ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new("str") +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + | + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:31:13 | LL | let _ = String::new().ends_with(".EXT12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:25:19 + --> $DIR/case_sensitive_file_extension_comparisons.rs:32:13 | LL | let _ = "str".ends_with(".EXT12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new("str") +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + | -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index 42ae227777c7..862234d204be 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -48,7 +48,7 @@ error: using `clone` on type `i32` which implements the `Copy` trait LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` -error: using `clone` on type `std::option::Option` which implements the `Copy` trait +error: using `clone` on type `Option` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:77:17 | LL | let value = opt.clone()?; // operator precedence needed (*opt)? diff --git a/tests/ui/dbg_macro.stderr b/tests/ui/dbg_macro.stderr index e6a65b46d975..ddb5f1342e99 100644 --- a/tests/ui/dbg_macro.stderr +++ b/tests/ui/dbg_macro.stderr @@ -1,143 +1,143 @@ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:5:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:9:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if n <= 1 { | ~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:10:9 | LL | dbg!(1) | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1 | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:12:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | n * factorial(n - 1) | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:17:5 | LL | dbg!(42); | ^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 42; | ~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:18:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | dbg!(dbg!(42)); | ~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:19:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:20:5 | LL | dbg!(1, 2, dbg!(3, 4)); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, dbg!(3, 4)); | ~~~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:21:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:41:9 | LL | dbg!(2); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 2; | ~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:47:5 | LL | dbg!(1); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1; | ~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:52:5 | LL | dbg!(1); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1; | ~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:58:9 | LL | dbg!(1); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1; | ~ diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index eedd43619392..5640599d48ae 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -12,17 +12,17 @@ use std::default::Default as D2; use std::string; fn main() { - let s1: String = std::string::String::default(); + let s1: String = String::default(); let s2 = String::default(); - let s3: String = std::string::String::default(); + let s3: String = String::default(); - let s4: String = std::string::String::default(); + let s4: String = String::default(); let s5 = string::String::default(); - let s6: String = std::string::String::default(); + let s6: String = String::default(); let s7 = std::string::String::default(); diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 49b2dde3f1e8..e4f73c08d190 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,8 +1,8 @@ -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:15:22 | LL | let s1: String = Default::default(); - | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^^^^^^ help: try: `String::default()` | note: the lint level is defined here --> $DIR/default_trait_access.rs:3:9 @@ -10,23 +10,23 @@ note: the lint level is defined here LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:19:22 | LL | let s3: String = D2::default(); - | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^ help: try: `String::default()` -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:21:22 | LL | let s4: String = std::default::Default::default(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:25:22 | LL | let s6: String = default::Default::default(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `GenericDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:35:46 diff --git a/tests/ui/derivable_impls.fixed b/tests/ui/derivable_impls.fixed index 7dcdfb0937e8..ee8456f5deb8 100644 --- a/tests/ui/derivable_impls.fixed +++ b/tests/ui/derivable_impls.fixed @@ -210,4 +210,25 @@ impl Default for IntOrString { } } +#[derive(Default)] +pub enum SimpleEnum { + Foo, + #[default] + Bar, +} + + + +pub enum NonExhaustiveEnum { + Foo, + #[non_exhaustive] + Bar, +} + +impl Default for NonExhaustiveEnum { + fn default() -> Self { + NonExhaustiveEnum::Bar + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs index 625cbcdde230..14af419bcad1 100644 --- a/tests/ui/derivable_impls.rs +++ b/tests/ui/derivable_impls.rs @@ -244,4 +244,27 @@ impl Default for IntOrString { } } +pub enum SimpleEnum { + Foo, + Bar, +} + +impl Default for SimpleEnum { + fn default() -> Self { + SimpleEnum::Bar + } +} + +pub enum NonExhaustiveEnum { + Foo, + #[non_exhaustive] + Bar, +} + +impl Default for NonExhaustiveEnum { + fn default() -> Self { + NonExhaustiveEnum::Bar + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.stderr b/tests/ui/derivable_impls.stderr index c1db5a58b1f5..81963c3be5b5 100644 --- a/tests/ui/derivable_impls.stderr +++ b/tests/ui/derivable_impls.stderr @@ -113,5 +113,26 @@ help: ...and instead derive it LL | #[derive(Default)] | -error: aborting due to 7 previous errors +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:252:1 + | +LL | / impl Default for SimpleEnum { +LL | | fn default() -> Self { +LL | | SimpleEnum::Bar +LL | | } +LL | | } + | |_^ + | + = help: remove the manual implementation... +help: ...and instead derive it... + | +LL | #[derive(Default)] + | +help: ...and mark the default variant + | +LL ~ #[default] +LL ~ Bar, + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index b276c384c04e..6e0ce55f57d9 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -86,4 +86,15 @@ impl Clone for GenericRef<'_, T, U> { } } +// https://github.com/rust-lang/rust-clippy/issues/10188 +#[repr(packed)] +#[derive(Copy)] +struct Packed(T); + +impl Clone for Packed { + fn clone(&self) -> Self { + *self + } +} + fn main() {} diff --git a/tests/ui/derive_hash_xor_eq.stderr b/tests/ui/derive_hash_xor_eq.stderr deleted file mode 100644 index 16c92397804e..000000000000 --- a/tests/ui/derive_hash_xor_eq.stderr +++ /dev/null @@ -1,59 +0,0 @@ -error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:12:10 - | -LL | #[derive(Hash)] - | ^^^^ - | -note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:15:1 - | -LL | impl PartialEq for Bar { - | ^^^^^^^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:21:10 - | -LL | #[derive(Hash)] - | ^^^^ - | -note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:24:1 - | -LL | impl PartialEq for Baz { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:33:1 - | -LL | / impl std::hash::Hash for Bah { -LL | | fn hash(&self, _: &mut H) {} -LL | | } - | |_^ - | -note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:30:10 - | -LL | #[derive(PartialEq)] - | ^^^^^^^^^ - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:51:5 - | -LL | / impl Hash for Foo3 { -LL | | fn hash(&self, _: &mut H) {} -LL | | } - | |_____^ - | -note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:48:14 - | -LL | #[derive(PartialEq)] - | ^^^^^^^^^ - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 4 previous errors - diff --git a/tests/ui/derive_hash_xor_eq.rs b/tests/ui/derived_hash_with_manual_eq.rs similarity index 62% rename from tests/ui/derive_hash_xor_eq.rs rename to tests/ui/derived_hash_with_manual_eq.rs index 813ddc566464..8ad09a8de43d 100644 --- a/tests/ui/derive_hash_xor_eq.rs +++ b/tests/ui/derived_hash_with_manual_eq.rs @@ -27,6 +27,8 @@ impl PartialEq for Baz { } } +// Implementing `Hash` with a derived `PartialEq` is fine. See #2627 + #[derive(PartialEq)] struct Bah; @@ -34,23 +36,4 @@ impl std::hash::Hash for Bah { fn hash(&self, _: &mut H) {} } -#[derive(PartialEq)] -struct Foo2; - -trait Hash {} - -// We don't want to lint on user-defined traits called `Hash` -impl Hash for Foo2 {} - -mod use_hash { - use std::hash::{Hash, Hasher}; - - #[derive(PartialEq)] - struct Foo3; - - impl Hash for Foo3 { - fn hash(&self, _: &mut H) {} - } -} - fn main() {} diff --git a/tests/ui/derived_hash_with_manual_eq.stderr b/tests/ui/derived_hash_with_manual_eq.stderr new file mode 100644 index 000000000000..230940f25fb6 --- /dev/null +++ b/tests/ui/derived_hash_with_manual_eq.stderr @@ -0,0 +1,29 @@ +error: you are deriving `Hash` but have implemented `PartialEq` explicitly + --> $DIR/derived_hash_with_manual_eq.rs:12:10 + | +LL | #[derive(Hash)] + | ^^^^ + | +note: `PartialEq` implemented here + --> $DIR/derived_hash_with_manual_eq.rs:15:1 + | +LL | impl PartialEq for Bar { + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::derived_hash_with_manual_eq)]` on by default + = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `Hash` but have implemented `PartialEq` explicitly + --> $DIR/derived_hash_with_manual_eq.rs:21:10 + | +LL | #[derive(Hash)] + | ^^^^ + | +note: `PartialEq` implemented here + --> $DIR/derived_hash_with_manual_eq.rs:24:1 + | +LL | impl PartialEq for Baz { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index 7de0b0bbdf9a..10044e65f115 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -72,3 +72,26 @@ fn test_owl_result_2() -> Result { produce_half_owl_ok().map(drop)?; Ok(1) } + +#[allow(unused)] +#[allow(clippy::unit_cmp)] +fn issue10122(x: u8) { + // This is a function which returns a reference and has a side-effect, which means + // that calling drop() on the function is considered an idiomatic way of achieving the side-effect + // in a match arm. + fn println_and(t: &T) -> &T { + println!("foo"); + t + } + + match x { + 0 => drop(println_and(&12)), // Don't lint (copy type), we only care about side-effects + 1 => drop(println_and(&String::new())), // Don't lint (no copy type), we only care about side-effects + 2 => { + drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block + }, + 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + 4 => drop(&2), // Lint, not a fn/method call + _ => (), + } +} diff --git a/tests/ui/drop_ref.stderr b/tests/ui/drop_ref.stderr index 4743cf79b5d3..293b9f6de832 100644 --- a/tests/ui/drop_ref.stderr +++ b/tests/ui/drop_ref.stderr @@ -107,5 +107,41 @@ note: argument has type `&SomeStruct` LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing + --> $DIR/drop_ref.rs:91:13 + | +LL | drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&i32` + --> $DIR/drop_ref.rs:91:18 + | +LL | drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing + --> $DIR/drop_ref.rs:93:14 + | +LL | 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&i32` + --> $DIR/drop_ref.rs:93:19 + | +LL | 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing + --> $DIR/drop_ref.rs:94:14 + | +LL | 4 => drop(&2), // Lint, not a fn/method call + | ^^^^^^^^ + | +note: argument has type `&i32` + --> $DIR/drop_ref.rs:94:19 + | +LL | 4 => drop(&2), // Lint, not a fn/method call + | ^^ + +error: aborting due to 12 previous errors diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 7367910eaa12..1f989bb12205 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -247,3 +247,24 @@ mod issue6312 { } } } + +struct Collection { + items: Vec, + len: usize, +} + +impl Default for Collection { + fn default() -> Self { + Self { + items: vec![1, 2, 3], + len: 0, + } + } +} + +#[allow(clippy::redundant_closure_call)] +fn issue10136() { + let mut c = Collection::default(); + // don't lint, since c.items was used to calculate this value + c.len = (|| c.items.len())(); +} diff --git a/tests/ui/iter_kv_map.fixed b/tests/ui/iter_kv_map.fixed index 83fee04080fa..f2a4c284cb16 100644 --- a/tests/ui/iter_kv_map.fixed +++ b/tests/ui/iter_kv_map.fixed @@ -1,14 +1,15 @@ // run-rustfix #![warn(clippy::iter_kv_map)] -#![allow(clippy::redundant_clone)] -#![allow(clippy::suspicious_map)] -#![allow(clippy::map_identity)] +#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] use std::collections::{BTreeMap, HashMap}; fn main() { let get_key = |(key, _val)| key; + fn ref_acceptor(v: &u32) -> u32 { + *v + } let map: HashMap = HashMap::new(); @@ -36,6 +37,20 @@ fn main() { let _ = map.keys().map(|key| key * 9).count(); let _ = map.values().map(|value| value * 17).count(); + // Preserve the ref in the fix. + let _ = map.clone().into_values().map(|ref val| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone().into_values().map(|mut val| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_values().count(); + let map: BTreeMap = BTreeMap::new(); let _ = map.keys().collect::>(); @@ -61,4 +76,18 @@ fn main() { // Lint let _ = map.keys().map(|key| key * 9).count(); let _ = map.values().map(|value| value * 17).count(); + + // Preserve the ref in the fix. + let _ = map.clone().into_values().map(|ref val| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone().into_values().map(|mut val| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_values().count(); } diff --git a/tests/ui/iter_kv_map.rs b/tests/ui/iter_kv_map.rs index 7a1f1fb0198c..ad6564df4084 100644 --- a/tests/ui/iter_kv_map.rs +++ b/tests/ui/iter_kv_map.rs @@ -1,14 +1,15 @@ // run-rustfix #![warn(clippy::iter_kv_map)] -#![allow(clippy::redundant_clone)] -#![allow(clippy::suspicious_map)] -#![allow(clippy::map_identity)] +#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] use std::collections::{BTreeMap, HashMap}; fn main() { let get_key = |(key, _val)| key; + fn ref_acceptor(v: &u32) -> u32 { + *v + } let map: HashMap = HashMap::new(); @@ -36,6 +37,22 @@ fn main() { let _ = map.iter().map(|(key, _value)| key * 9).count(); let _ = map.iter().map(|(_key, value)| value * 17).count(); + // Preserve the ref in the fix. + let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone() + .into_iter() + .map(|(_, mut val)| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); + let map: BTreeMap = BTreeMap::new(); let _ = map.iter().map(|(key, _)| key).collect::>(); @@ -61,4 +78,20 @@ fn main() { // Lint let _ = map.iter().map(|(key, _value)| key * 9).count(); let _ = map.iter().map(|(_key, value)| value * 17).count(); + + // Preserve the ref in the fix. + let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone() + .into_iter() + .map(|(_, mut val)| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); } diff --git a/tests/ui/iter_kv_map.stderr b/tests/ui/iter_kv_map.stderr index 9b9b04c97d81..e00da223b4dd 100644 --- a/tests/ui/iter_kv_map.stderr +++ b/tests/ui/iter_kv_map.stderr @@ -1,5 +1,5 @@ error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:15:13 + --> $DIR/iter_kv_map.rs:16:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` @@ -7,130 +7,198 @@ LL | let _ = map.iter().map(|(key, _)| key).collect::>(); = note: `-D clippy::iter-kv-map` implied by `-D warnings` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:16:13 + --> $DIR/iter_kv_map.rs:17:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:17:13 + --> $DIR/iter_kv_map.rs:18:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:19:13 + --> $DIR/iter_kv_map.rs:20:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:20:13 + --> $DIR/iter_kv_map.rs:21:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:22:13 + --> $DIR/iter_kv_map.rs:23:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:23:13 + --> $DIR/iter_kv_map.rs:24:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:25:13 + --> $DIR/iter_kv_map.rs:26:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:26:13 + --> $DIR/iter_kv_map.rs:27:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:36:13 + --> $DIR/iter_kv_map.rs:37:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:37:13 + --> $DIR/iter_kv_map.rs:38:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` -error: iterating on a map's keys +error: iterating on a map's values --> $DIR/iter_kv_map.rs:41:13 | +LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:44:13 + | +LL | let _ = map + | _____________^ +LL | | .clone() +LL | | .into_iter() +LL | | .map(|(_, mut val)| { +LL | | val += 2; +LL | | val +LL | | }) + | |__________^ + | +help: try + | +LL ~ let _ = map +LL + .clone().into_values().map(|mut val| { +LL + val += 2; +LL + val +LL + }) + | + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:54:13 + | +LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:58:13 + | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:42:13 + --> $DIR/iter_kv_map.rs:59:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:43:13 + --> $DIR/iter_kv_map.rs:60:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:45:13 + --> $DIR/iter_kv_map.rs:62:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:46:13 + --> $DIR/iter_kv_map.rs:63:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:48:13 + --> $DIR/iter_kv_map.rs:65:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:49:13 + --> $DIR/iter_kv_map.rs:66:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:51:13 + --> $DIR/iter_kv_map.rs:68:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:52:13 + --> $DIR/iter_kv_map.rs:69:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:62:13 + --> $DIR/iter_kv_map.rs:79:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:63:13 + --> $DIR/iter_kv_map.rs:80:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` -error: aborting due to 22 previous errors +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:83:13 + | +LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:86:13 + | +LL | let _ = map + | _____________^ +LL | | .clone() +LL | | .into_iter() +LL | | .map(|(_, mut val)| { +LL | | val += 2; +LL | | val +LL | | }) + | |__________^ + | +help: try + | +LL ~ let _ = map +LL + .clone().into_values().map(|mut val| { +LL + val += 2; +LL + val +LL + }) + | + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:96:13 + | +LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` + +error: aborting due to 28 previous errors diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 31e1cb6c3d7f..4cb7f6b687f1 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes, lint_reasons, rustc_private)] +#![feature(lint_reasons)] #![allow( unused, clippy::uninlined_format_args, @@ -491,14 +491,3 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } - -extern crate rustc_lint; -extern crate rustc_span; - -#[allow(dead_code)] -mod span_lint { - use rustc_lint::{LateContext, Lint, LintContext}; - fn foo(cx: &LateContext<'_>, lint: &'static Lint) { - cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(String::new())); - } -} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 55c2738fcf27..9a01190ed8db 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,5 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes, lint_reasons, rustc_private)] +#![feature(lint_reasons)] #![allow( unused, clippy::uninlined_format_args, @@ -491,14 +491,3 @@ mod issue_9782_method_variant { S.foo::<&[u8; 100]>(&a); } } - -extern crate rustc_lint; -extern crate rustc_span; - -#[allow(dead_code)] -mod span_lint { - use rustc_lint::{LateContext, Lint, LintContext}; - fn foo(cx: &LateContext<'_>, lint: &'static Lint) { - cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); - } -} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 98a48d68317b..d26c317124b8 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -216,11 +216,5 @@ error: the borrowed expression implements the required traits LL | foo(&a); | ^^ help: change this to: `a` -error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:502:85 - | -LL | cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new())); - | ^^^^^^^^^^^^^^ help: change this to: `String::new()` - -error: aborting due to 37 previous errors +error: aborting due to 36 previous errors diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d451be1f389a..ab1c0e590bbc 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -277,4 +277,14 @@ fn issue9947() -> Result<(), String> { do yeet "hello"; } +// without anyhow, but triggers the same bug I believe +#[expect(clippy::useless_format)] +fn issue10051() -> Result { + if true { + Ok(format!("ok!")) + } else { + Err(format!("err!")) + } +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index e1a1bea2c0b8..abed338bb9b2 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -287,4 +287,14 @@ fn issue9947() -> Result<(), String> { do yeet "hello"; } +// without anyhow, but triggers the same bug I believe +#[expect(clippy::useless_format)] +fn issue10051() -> Result { + if true { + return Ok(format!("ok!")); + } else { + return Err(format!("err!")); + } +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index ca2253e65863..52eabf6e1370 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -386,5 +386,21 @@ LL | let _ = 42; return; | = help: remove `return` -error: aborting due to 46 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:294:9 + | +LL | return Ok(format!("ok!")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:296:9 + | +LL | return Err(format!("err!")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: aborting due to 48 previous errors diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index a157b6a6f9ad..00b427450935 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -239,9 +239,3 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } - -#[allow(unused, clippy::manual_retain)] -fn possible_borrower_improvements() { - let mut s = String::from("foobar"); - s = s.chars().filter(|&c| c != 'o').collect(); -} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index 430672e8b8df..f899127db8d0 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -239,9 +239,3 @@ fn false_negative_5707() { let _z = x.clone(); // pr 7346 can't lint on `x` drop(y); } - -#[allow(unused, clippy::manual_retain)] -fn possible_borrower_improvements() { - let mut s = String::from("foobar"); - s = s.chars().filter(|&c| c != 'o').to_owned().collect(); -} diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 1bacc2c76af1..782590034d05 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -179,17 +179,5 @@ note: this value is dropped without further use LL | foo(&x.clone(), move || { | ^ -error: redundant clone - --> $DIR/redundant_clone.rs:246:40 - | -LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); - | ^^^^^^^^^^^ help: remove this - | -note: this value is dropped without further use - --> $DIR/redundant_clone.rs:246:9 - | -LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 16 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 2f76b5752960..5076f61334d6 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -10,6 +10,7 @@ #![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] +#![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] @@ -45,6 +46,7 @@ #![warn(clippy::box_collection)] #![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::cognitive_complexity)] +#![warn(clippy::derived_hash_with_manual_eq)] #![warn(clippy::disallowed_methods)] #![warn(clippy::disallowed_types)] #![warn(clippy::mixed_read_write_in_expression)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index 699c0ff464e9..64bc1ca7116c 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -10,6 +10,7 @@ #![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] +#![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] @@ -45,6 +46,7 @@ #![warn(clippy::box_vec)] #![warn(clippy::const_static_lifetime)] #![warn(clippy::cyclomatic_complexity)] +#![warn(clippy::derive_hash_xor_eq)] #![warn(clippy::disallowed_method)] #![warn(clippy::disallowed_type)] #![warn(clippy::eval_order_dependence)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 9af58dc75a68..27a0263292ef 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:41:9 + --> $DIR/rename.rs:42:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -7,244 +7,250 @@ LL | #![warn(clippy::almost_complete_letter_range)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:42:9 + --> $DIR/rename.rs:43:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:43:9 + --> $DIR/rename.rs:44:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:44:9 + --> $DIR/rename.rs:45:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:45:9 + --> $DIR/rename.rs:46:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:46:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:47:9 + --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` +error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` + --> $DIR/rename.rs:49:9 + | +LL | #![warn(clippy::derive_hash_xor_eq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` + error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:48:9 + --> $DIR/rename.rs:50:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:49:9 + --> $DIR/rename.rs:51:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:53:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 41 previous errors +error: aborting due to 42 previous errors diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed index 63d31ff83f9b..a0dcc0172e8b 100644 --- a/tests/ui/single_element_loop.fixed +++ b/tests/ui/single_element_loop.fixed @@ -33,4 +33,31 @@ fn main() { let item = 0..5; dbg!(item); } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + continue; + } + } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + break; + } + } + + // should lint (issue #10018) + { + let _ = 42; + let _f = |n: u32| { + for i in 0..n { + if i > 10 { + dbg!(i); + break; + } + } + }; + } } diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs index 2cda5a329d25..bc014035c98a 100644 --- a/tests/ui/single_element_loop.rs +++ b/tests/ui/single_element_loop.rs @@ -27,4 +27,30 @@ fn main() { for item in [0..5].into_iter() { dbg!(item); } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + continue; + } + } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + break; + } + } + + // should lint (issue #10018) + for _ in [42] { + let _f = |n: u32| { + for i in 0..n { + if i > 10 { + dbg!(i); + break; + } + } + }; + } } diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr index 0aeb8da1a2e2..14437a59745e 100644 --- a/tests/ui/single_element_loop.stderr +++ b/tests/ui/single_element_loop.stderr @@ -95,5 +95,32 @@ LL + dbg!(item); LL + } | -error: aborting due to 6 previous errors +error: for loop over a single element + --> $DIR/single_element_loop.rs:46:5 + | +LL | / for _ in [42] { +LL | | let _f = |n: u32| { +LL | | for i in 0..n { +LL | | if i > 10 { +... | +LL | | }; +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let _ = 42; +LL + let _f = |n: u32| { +LL + for i in 0..n { +LL + if i > 10 { +LL + dbg!(i); +LL + break; +LL + } +LL + } +LL + }; +LL + } + | + +error: aborting due to 7 previous errors diff --git a/tests/ui/suspicious_to_owned.stderr b/tests/ui/suspicious_to_owned.stderr index ae1aec34d82e..dec3f50d6f1b 100644 --- a/tests/ui/suspicious_to_owned.stderr +++ b/tests/ui/suspicious_to_owned.stderr @@ -1,4 +1,4 @@ -error: this `to_owned` call clones the std::borrow::Cow<'_, str> itself and does not cause the std::borrow::Cow<'_, str> contents to become owned +error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned --> $DIR/suspicious_to_owned.rs:16:13 | LL | let _ = cow.to_owned(); @@ -6,19 +6,19 @@ LL | let _ = cow.to_owned(); | = note: `-D clippy::suspicious-to-owned` implied by `-D warnings` -error: this `to_owned` call clones the std::borrow::Cow<'_, [char; 3]> itself and does not cause the std::borrow::Cow<'_, [char; 3]> contents to become owned +error: this `to_owned` call clones the Cow<'_, [char; 3]> itself and does not cause the Cow<'_, [char; 3]> contents to become owned --> $DIR/suspicious_to_owned.rs:26:13 | LL | let _ = cow.to_owned(); | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` -error: this `to_owned` call clones the std::borrow::Cow<'_, std::vec::Vec> itself and does not cause the std::borrow::Cow<'_, std::vec::Vec> contents to become owned +error: this `to_owned` call clones the Cow<'_, Vec> itself and does not cause the Cow<'_, Vec> contents to become owned --> $DIR/suspicious_to_owned.rs:36:13 | LL | let _ = cow.to_owned(); | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` -error: this `to_owned` call clones the std::borrow::Cow<'_, str> itself and does not cause the std::borrow::Cow<'_, str> contents to become owned +error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned --> $DIR/suspicious_to_owned.rs:46:13 | LL | let _ = cow.to_owned(); diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 94cc7777acac..6022d9fa4c5c 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -38,13 +38,13 @@ LL | t.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on type `std::option::Option` which implements the `Copy` trait +error: using `clone` on type `Option` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:42:5 | LL | Some(t).clone(); | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` -error: using `clone` on a double-reference; this will copy the reference of type `&std::vec::Vec` instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&Vec` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:48:22 | LL | let z: &Vec<_> = y.clone(); @@ -57,10 +57,10 @@ LL | let z: &Vec<_> = &(*y).clone(); | ~~~~~~~~~~~~~ help: or try being explicit if you are sure, that you want to clone a reference | -LL | let z: &Vec<_> = <&std::vec::Vec>::clone(y); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let z: &Vec<_> = <&Vec>::clone(y); + | ~~~~~~~~~~~~~~~~~~~~~ -error: using `clone` on type `many_derefs::E` which implements the `Copy` trait +error: using `clone` on type `E` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:84:20 | LL | let _: E = a.clone(); diff --git a/tests/ui/unused_self.rs b/tests/ui/unused_self.rs index 92e8e1dba69d..55bd5607185c 100644 --- a/tests/ui/unused_self.rs +++ b/tests/ui/unused_self.rs @@ -60,6 +60,16 @@ mod unused_self_allow { // shouldn't trigger for public methods pub fn unused_self_move(self) {} } + + pub struct E; + + impl E { + // shouldn't trigger if body contains todo!() + pub fn unused_self_todo(self) { + let x = 42; + todo!() + } + } } pub use unused_self_allow::D; diff --git a/tests/ui/unused_self.stderr b/tests/ui/unused_self.stderr index 23186122a9af..919f9b6efdab 100644 --- a/tests/ui/unused_self.stderr +++ b/tests/ui/unused_self.stderr @@ -4,7 +4,7 @@ error: unused `self` argument LL | fn unused_self_move(self) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function = note: `-D clippy::unused-self` implied by `-D warnings` error: unused `self` argument @@ -13,7 +13,7 @@ error: unused `self` argument LL | fn unused_self_ref(&self) {} | ^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:13:32 @@ -21,7 +21,7 @@ error: unused `self` argument LL | fn unused_self_mut_ref(&mut self) {} | ^^^^^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:14:32 @@ -29,7 +29,7 @@ error: unused `self` argument LL | fn unused_self_pin_ref(self: Pin<&Self>) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:15:36 @@ -37,7 +37,7 @@ error: unused `self` argument LL | fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:16:35 @@ -45,7 +45,7 @@ error: unused `self` argument LL | fn unused_self_pin_nested(self: Pin>) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:17:28 @@ -53,7 +53,7 @@ error: unused `self` argument LL | fn unused_self_box(self: Box) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:18:40 @@ -61,7 +61,7 @@ error: unused `self` argument LL | fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 { | ^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:21:37 @@ -69,7 +69,7 @@ error: unused `self` argument LL | fn unused_self_class_method(&self) { | ^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: aborting due to 9 previous errors From b38848d8f740a7ada6b2ba14acf08c5e57c94002 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 12 Jan 2023 13:00:03 -0500 Subject: [PATCH 456/524] Fix suggestion in `transmutes_expressible_as_ptr_casts` when the source type is a borrow. --- clippy_lints/src/transmute/mod.rs | 7 ++- .../transmutes_expressible_as_ptr_casts.rs | 58 ++++++++++++------- clippy_lints/src/transmute/utils.rs | 24 ++------ .../transmutes_expressible_as_ptr_casts.fixed | 2 + .../ui/transmutes_expressible_as_ptr_casts.rs | 2 + ...transmutes_expressible_as_ptr_casts.stderr | 10 +++- 6 files changed, 60 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 691d759d7739..c0d290b5adc4 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -479,7 +479,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { // - char conversions (https://github.com/rust-lang/rust/issues/89259) let const_context = in_constant(cx, e.hir_id); - let from_ty = cx.typeck_results().expr_ty_adjusted(arg); + let (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) { + [] => (cx.typeck_results().expr_ty(arg), false), + [.., a] => (a.target, true), + }; // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them. let to_ty = cx.typeck_results().expr_ty(e); @@ -506,7 +509,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { ); if !linted { - transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, arg); + transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg); } } } diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index b79d4e915a27..8530b43243fa 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,11 +1,11 @@ -use super::utils::can_be_expressed_as_pointer_cast; +use super::utils::check_cast; use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{cast::CastKind, Ty}; /// Checks for `transmutes_expressible_as_ptr_casts` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -13,24 +13,40 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, + from_ty_adjusted: bool, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, ) -> bool { - if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) { - span_lint_and_then( - cx, - TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - e.span, - &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg = arg.as_ty(to_ty.to_string()).to_string(); - diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); - } - }, - ); - true - } else { - false - } + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; + let mut app = Applicability::MachineApplicable; + let sugg = match check_cast(cx, e, from_ty, to_ty) { + Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => { + Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app) + .as_ty(to_ty.to_string()) + .to_string() + }, + Some(PtrAddrCast) if !from_ty_adjusted => Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app) + .as_ty(to_ty.to_string()) + .to_string(), + + // The only adjustments here would be ref-to-ptr and unsize coercions. The result of an unsize coercions can't + // be transmuted to a usize. For ref-to-ptr coercions, borrows need to be cast to a pointer before being cast to + // a usize. + Some(PtrAddrCast) => format!( + "{} as {to_ty}", + Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app).as_ty(from_ty) + ), + _ => return false, + }; + + span_lint_and_sugg( + cx, + TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + e.span, + &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), + "try", + sugg, + app, + ); + true } diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 49d863ec03f1..c93f047f5da2 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -20,28 +20,16 @@ pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx } } -/// Check if the type conversion can be expressed as a pointer cast, instead of -/// a transmute. In certain cases, including some invalid casts from array -/// references to pointers, this may cause additional errors to be emitted and/or -/// ICE error messages. This function will panic if that occurs. -pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, -) -> bool { - use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; - matches!( - check_cast(cx, e, from_ty, to_ty), - Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) - ) -} - /// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of /// the cast. In certain cases, including some invalid casts from array references /// to pointers, this may cause additional errors to be emitted and/or ICE error /// messages. This function will panic if that occurs. -fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option { +pub(super) fn check_cast<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) -> Option { let hir_id = e.hir_id; let local_def_id = hir_id.owner.def_id; diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 7263abac15df..55307506eb3c 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -51,6 +51,8 @@ fn main() { // e is a function pointer type and U is an integer; fptr-addr-cast let _usize_from_fn_ptr_transmute = unsafe { foo as usize }; let _usize_from_fn_ptr = foo as *const usize; + + let _usize_from_ref = unsafe { &1u32 as *const u32 as usize }; } // If a ref-to-ptr cast of this form where the pointer type points to a type other diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.rs b/tests/ui/transmutes_expressible_as_ptr_casts.rs index d8e4421d4c18..e7360f3f9dcb 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -51,6 +51,8 @@ fn main() { // e is a function pointer type and U is an integer; fptr-addr-cast let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; let _usize_from_fn_ptr = foo as *const usize; + + let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) }; } // If a ref-to-ptr cast of this form where the pointer type points to a type other diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/tests/ui/transmutes_expressible_as_ptr_casts.stderr index de9418c8d1ad..e862fcb67a4a 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -46,11 +46,17 @@ error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` +error: transmute from `*const u32` to `usize` which could be expressed as a pointer cast instead + --> $DIR/transmutes_expressible_as_ptr_casts.rs:55:36 + | +LL | let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&1u32 as *const u32 as usize` + error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:14 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:66:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors From 2253646395c70c525368c29399fb4bb91fb95438 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 8 Jan 2023 23:27:22 +0000 Subject: [PATCH 457/524] Don't suggest dyn as parameter to add --- tests/ui/crashes/ice-6252.stderr | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/ui/crashes/ice-6252.stderr b/tests/ui/crashes/ice-6252.stderr index 638e4a548493..efdd56dd47d3 100644 --- a/tests/ui/crashes/ice-6252.stderr +++ b/tests/ui/crashes/ice-6252.stderr @@ -17,9 +17,12 @@ error[E0412]: cannot find type `VAL` in this scope --> $DIR/ice-6252.rs:10:63 | LL | impl TypeVal for Multiply where N: TypeVal {} - | - ^^^ not found in this scope - | | - | help: you might be missing a type parameter: `, VAL` + | ^^^ not found in this scope + | +help: you might be missing a type parameter + | +LL | impl TypeVal for Multiply where N: TypeVal {} + | +++++ error[E0046]: not all trait items implemented, missing: `VAL` --> $DIR/ice-6252.rs:10:1 From 295225e9cdb69fa29986cd07d38fd54a16ca2aa0 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 13 Jan 2023 00:04:28 +0000 Subject: [PATCH 458/524] Move `unchecked_duration_subtraction` to pedantic --- clippy_lints/src/instant_subtraction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index dd1b23e7d9d2..9f6e89405713 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -61,7 +61,7 @@ declare_clippy_lint! { /// [`Instant::now()`]: std::time::Instant::now; #[clippy::version = "1.65.0"] pub UNCHECKED_DURATION_SUBTRACTION, - suspicious, + pedantic, "finds unchecked subtraction of a 'Duration' from an 'Instant'" } From d43dce14d5eb7af1656d730bfbb5918c84addbca Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Fri, 13 Jan 2023 08:59:00 -0700 Subject: [PATCH 459/524] Remove cognitive-complexity-threshold from docs --- README.md | 1 - book/src/configuration.md | 1 - 2 files changed, 2 deletions(-) diff --git a/README.md b/README.md index f7e03ca4cd8c..c78ae06765d3 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,6 @@ value` mapping e.g. ```toml avoid-breaking-exported-api = false disallowed-names = ["toto", "tata", "titi"] -cognitive-complexity-threshold = 30 ``` See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), diff --git a/book/src/configuration.md b/book/src/configuration.md index bbe1081ccad9..fac3e438c543 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -8,7 +8,6 @@ basic `variable = value` mapping eg. ```toml avoid-breaking-exported-api = false disallowed-names = ["toto", "tata", "titi"] -cognitive-complexity-threshold = 30 ``` See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), From 2e2ae68d5a3ef0b8c0c968fd6ec4e859c77ed2f1 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Fri, 13 Jan 2023 11:38:34 -0700 Subject: [PATCH 460/524] Document lint configuration values in Clippy's book Signed-off-by: Tyler Weaver --- .github/workflows/remark.yml | 3 + README.md | 3 + book/src/SUMMARY.md | 1 + book/src/configuration.md | 3 + book/src/lint_configuration.md | 52 ++++++++++++++++ .../internal_lints/metadata_collector.rs | 62 +++++++++++++++++-- 6 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 book/src/lint_configuration.md diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 81ef072bbb07..22925a753dfe 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -33,6 +33,9 @@ jobs: echo `pwd`/mdbook >> $GITHUB_PATH # Run + - name: cargo collect-metadata + run: cargo collect-metadata + - name: Check *.md files run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null diff --git a/README.md b/README.md index c78ae06765d3..2ba095368ebf 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,9 @@ disallowed-names = ["toto", "tata", "titi"] See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), the lint descriptions contain the names and meanings of these configuration variables. +See [table of lint configurations](https://doc.rust-lang.org/nightly/clippy/lint_configuration.html) +to see what configuration options you can set and the lints they configure. + For configurations that are a list type with default values such as [disallowed-names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names), you can use the unique value `".."` to extend the default values instead of replacing them. diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 1f0b8db28a15..0649f7a631df 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -5,6 +5,7 @@ - [Installation](installation.md) - [Usage](usage.md) - [Configuration](configuration.md) + - [Lint Configuration](lint_configuration.md) - [Clippy's Lints](lints.md) - [Continuous Integration](continuous_integration/README.md) - [GitHub Actions](continuous_integration/github_actions.md) diff --git a/book/src/configuration.md b/book/src/configuration.md index fac3e438c543..e68f30be43cd 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -13,6 +13,9 @@ disallowed-names = ["toto", "tata", "titi"] See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), the lint descriptions contain the names and meanings of these configuration variables. +See [table of lint configurations](./lint_configuration.md) +to see what configuration options you can set and the lints they configure. + For configurations that are a list type with default values such as [disallowed-names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names), you can use the unique value `".."` to extend the default values instead of replacing them. diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md new file mode 100644 index 000000000000..102ed7ad2cea --- /dev/null +++ b/book/src/lint_configuration.md @@ -0,0 +1,52 @@ +## Configuration Options + +| Option | Default | Description | Lints | +|--|--|--|--| +| arithmetic-side-effects-allowed | `{}` | Suppress checking of the passed type names in all types of operations | [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) | +| arithmetic-side-effects-allowed-binary | `[]` | Suppress checking of the passed type pair names in binary operations like addition or multiplication | [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) | +| arithmetic-side-effects-allowed-unary | `{}` | Suppress checking of the passed type names in unary operations like "negation" (`-`) | [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) | +| avoid-breaking-exported-api | `true` | Suppress lints whenever the suggested change would cause breakage for other crates | [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) [unnecessary_wraps](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps) [unused_self](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self) [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) [wrong_self_convention](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention) [box_collection](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection) [redundant_allocation](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation) [rc_buffer](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer) [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) [option_option](https://rust-lang.github.io/rust-clippy/master/index.html#option_option) [linkedlist](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist) [rc_mutex](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex) | +| msrv | `None` | The minimum rust version that the project supports | [manual_split_once](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) [manual_str_repeat](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) [cloned_instead_of_copied](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied) [redundant_field_names](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) [redundant_static_lifetimes](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) [filter_map_next](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next) [checked_conversions](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions) [manual_range_contains](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains) [use_self](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) [mem_replace_with_default](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) [manual_non_exhaustive](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) [option_as_ref_deref](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) [map_unwrap_or](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or) [match_like_matches_macro](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro) [manual_strip](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) [missing_const_for_fn](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) [unnested_or_patterns](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) [from_over_into](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) [ptr_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) [if_then_some_else_none](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) [approx_constant](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant) [deprecated_cfg_attr](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr) [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) [map_clone](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) [borrow_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr) [manual_bits](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) [err_expect](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect) [cast_abs_to_unsigned](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned) [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) | +| cognitive-complexity-threshold | `25` | The maximum cognitive complexity a function can have | [cognitive_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity) | +| disallowed-names | `["foo", "baz", "quux"]` | The list of disallowed names to lint about | [disallowed_names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names) | +| doc-valid-idents | `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` | The list of words this lint should not consider as identifiers needing ticks | [doc_markdown](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown) | +| too-many-arguments-threshold | `7` | The maximum number of argument a function or method can have | [too_many_arguments](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments) | +| type-complexity-threshold | `250` | The maximum complexity a type can have | [type_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity) | +| single-char-binding-names-threshold | `4` | The maximum number of single char bindings a scope may have | [many_single_char_names](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names) | +| too-large-for-stack | `200` | The maximum size of objects (in bytes) that will be linted | [boxed_local](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local) [useless_vec](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec) | +| enum-variant-name-threshold | `3` | The minimum number of enum variants for the lints about variant names to trigger | [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) | +| enum-variant-size-threshold | `200` | The maximum size of an enum's variant to avoid box suggestion | [large_enum_variant](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant) | +| verbose-bit-mask-threshold | `1` | The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' | [verbose_bit_mask](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask) | +| literal-representation-threshold | `16384` | The lower bound for linting decimal literals | [decimal_literal_representation](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation) | +| trivial-copy-size-limit | `None` | The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference | [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) | +| pass-by-value-size-limit | `256` | The minimum size (in bytes) to consider a type for passing by reference instead of by value | [large_type_pass_by_move](https://rust-lang.github.io/rust-clippy/master/index.html#large_type_pass_by_move) | +| too-many-lines-threshold | `100` | The maximum number of lines a function or method can have | [too_many_lines](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) | +| array-size-threshold | `512000` | The maximum allowed size for arrays on the stack | [large_stack_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays) [large_const_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays) | +| vec-box-size-threshold | `4096` | The size of the boxed type in bytes, where boxing in a `Vec` is allowed | [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) | +| max-trait-bounds | `3` | The maximum number of bounds a trait can have to be linted | [type_repetition_in_bounds](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) | +| max-struct-bools | `3` | The maximum number of bool fields a struct can have | [struct_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools) | +| max-fn-params-bools | `3` | The maximum number of bool parameters a function can have | [fn_params_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools) | +| warn-on-all-wildcard-imports | `false` | Whether to allow certain wildcard imports (prelude, super in tests) | [wildcard_imports](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) | +| disallowed-macros | `[]` | The list of disallowed macros, written as fully qualified paths | [disallowed_macros](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros) | +| disallowed-methods | `[]` | The list of disallowed methods, written as fully qualified paths | [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) | +| disallowed-types | `[]` | The list of disallowed types, written as fully qualified paths | [disallowed_types](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) | +| unreadable-literal-lint-fractions | `true` | Should the fraction of a decimal be linted to include separators | [unreadable_literal](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal) | +| upper-case-acronyms-aggressive | `false` | Enables verbose mode | [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) | +| matches-for-let-else | `WellKnownTypes` | Whether the matches should be considered by the lint, and whether there should be filtering for common types | [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) | +| cargo-ignore-publish | `false` | For internal testing only, ignores the current `publish` settings in the Cargo manifest | [_cargo_common_metadata](https://rust-lang.github.io/rust-clippy/master/index.html#_cargo_common_metadata) | +| standard-macro-braces | `[]` | Enforce the named macros always use the braces specified | [nonstandard_macro_braces](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces) | +| enforced-import-renames | `[]` | The list of imports to always rename, a fully qualified path followed by the rename | [missing_enforced_import_renames](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames) | +| allowed-scripts | `["Latin"]` | The list of unicode scripts allowed to be used in the scope | [disallowed_script_idents](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents) | +| enable-raw-pointer-heuristic-for-send | `true` | Whether to apply the raw pointer heuristic to determine if a type is `Send` | [non_send_fields_in_send_ty](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty) | +| max-suggested-slice-pattern-length | `3` | When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in the slice pattern that is suggested | [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) | +| await-holding-invalid-types | `[]` | [ERROR] MALFORMED DOC COMMENT | | +| max-include-file-size | `1000000` | The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes | [large_include_file](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file) | +| allow-expect-in-tests | `false` | Whether `expect` should be allowed within `#[cfg(test)]` | [expect_used](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used) | +| allow-unwrap-in-tests | `false` | Whether `unwrap` should be allowed in test cfg | [unwrap_used](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) | +| allow-dbg-in-tests | `false` | Whether `dbg!` should be allowed in test functions | [dbg_macro](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) | +| allow-print-in-tests | `false` | Whether print macros (ex | [print_stdout](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout) [print_stderr](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr) | +| large-error-threshold | `128` | The maximum size of the `Err`-variant in a `Result` returned from a function | [result_large_err](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) | +| ignore-interior-mutability | `["bytes::Bytes"]` | A list of paths to types that should be treated like `Arc`, i | [mutable_key](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key) | +| allow-mixed-uninlined-format-args | `true` | Whether to allow mixed uninlined format args, e | [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) | +| suppress-restriction-lint-in-const | `false` | In same cases the restructured operation might not be unavoidable, as the suggested counterparts are unavailable in constant code | [indexing_slicing](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing) | + diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 929544cd69d5..c74f3b6e3ad6 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -14,6 +14,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths}; use if_chain::if_chain; +use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ @@ -34,8 +35,10 @@ use std::path::Path; use std::path::PathBuf; use std::process::Command; -/// This is the output file of the lint collector. -const OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; +/// This is the json output file of the lint collector. +const JSON_OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; +/// This is the markdown output file of the lint collector. +const MARKDOWN_OUTPUT_FILE: &str = "../book/src/lint_configuration.md"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"]; /// These groups will be ignored by the lint group matcher. This is useful for collections like @@ -176,6 +179,14 @@ This lint has the following configuration variables: ) }) } + + fn get_markdown_table(&self) -> String { + self.config + .iter() + .filter(|config| config.deprecation_reason.is_none()) + .map(ClippyConfiguration::to_markdown_table_entry) + .join("\n") + } } impl Drop for MetadataCollector { @@ -199,12 +210,32 @@ impl Drop for MetadataCollector { collect_renames(&mut lints); - // Outputting - if Path::new(OUTPUT_FILE).exists() { - fs::remove_file(OUTPUT_FILE).unwrap(); + // Outputting json + if Path::new(JSON_OUTPUT_FILE).exists() { + fs::remove_file(JSON_OUTPUT_FILE).unwrap(); } - let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap(); + let mut file = OpenOptions::new() + .write(true) + .create(true) + .open(JSON_OUTPUT_FILE) + .unwrap(); writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap(); + + // Outputting markdown + if Path::new(MARKDOWN_OUTPUT_FILE).exists() { + fs::remove_file(MARKDOWN_OUTPUT_FILE).unwrap(); + } + let mut file = OpenOptions::new() + .write(true) + .create(true) + .open(MARKDOWN_OUTPUT_FILE) + .unwrap(); + writeln!( + file, + "## Lint Configuration\n\n| Option | Default | Description | Lints |\n|--|--|--|--|\n{}\n", + self.get_markdown_table() + ) + .unwrap(); } } @@ -505,6 +536,25 @@ impl ClippyConfiguration { deprecation_reason, } } + + fn to_markdown_table_entry(&self) -> String { + format!( + "| {} | `{}` | {} | {} |", + self.name, + self.default, + self.doc + .split('.') + .next() + .unwrap_or("") + .replace('|', "\\|") + .replace("\n ", " "), + self.lints + .iter() + .map(|name| name.to_string().split_whitespace().next().unwrap().to_string()) + .map(|name| format!("[{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) + .join(" ") + ) + } } fn collect_configs() -> Vec { From 93d0f470640de319c9cad441509cfd0f55d84172 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 30 Nov 2022 20:41:02 +0000 Subject: [PATCH 461/524] Check ADT fields for copy implementations considering regions --- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 1249db5dc479..8c9d4c5cfe66 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -24,7 +24,7 @@ use rustc_span::symbol::kw; use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::misc::can_type_implement_copy; +use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; use std::borrow::Cow; declare_clippy_lint! { @@ -200,7 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { let sugg = |diag: &mut Diagnostic| { if let ty::Adt(def, ..) = ty.kind() { if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { - if can_type_implement_copy( + if type_allowed_to_implement_copy( cx.tcx, cx.param_env, ty, From 93f602f1cf324e89ec1312583cb033eeb977fa73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maria=20Jos=C3=A9=20Solano?= Date: Fri, 13 Jan 2023 15:21:49 -0800 Subject: [PATCH 462/524] Add missing arguments to cargo lint example --- book/src/development/adding_lints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 8b4eee8c9d94..145b393b7535 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -146,7 +146,7 @@ For cargo lints, the process of testing differs in that we are interested in the manifest. If our new lint is named e.g. `foo_categories`, after running `cargo dev -new_lint` we will find by default two new crates, each with its manifest file: +new_lint --name=foo_categories --type=cargo --category=cargo` we will find by default two new crates, each with its manifest file: * `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. From 7d1609dce356b9b603702f1ba0011f2fee949787 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Fri, 13 Jan 2023 15:32:04 -0700 Subject: [PATCH 463/524] Document configurations in table and paragraphs Signed-off-by: Tyler Weaver --- README.md | 9 +- book/src/configuration.md | 9 +- book/src/lint_configuration.md | 568 ++++++++++++++++-- clippy_lints/src/utils/conf.rs | 3 +- .../internal_lints/metadata_collector.rs | 43 +- 5 files changed, 552 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 2ba095368ebf..ab44db694835 100644 --- a/README.md +++ b/README.md @@ -196,11 +196,10 @@ avoid-breaking-exported-api = false disallowed-names = ["toto", "tata", "titi"] ``` -See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), -the lint descriptions contain the names and meanings of these configuration variables. - -See [table of lint configurations](https://doc.rust-lang.org/nightly/clippy/lint_configuration.html) -to see what configuration options you can set and the lints they configure. +The [table of configurations](https://doc.rust-lang.org/nightly/clippy/lint_configuration.html) +contains all config values, their default, and a list of lints they affect. +Each [configurable lint](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration) +, also contains information about these values. For configurations that are a list type with default values such as [disallowed-names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names), diff --git a/book/src/configuration.md b/book/src/configuration.md index e68f30be43cd..87f4a697af9f 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -10,11 +10,10 @@ avoid-breaking-exported-api = false disallowed-names = ["toto", "tata", "titi"] ``` -See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration), -the lint descriptions contain the names and meanings of these configuration variables. - -See [table of lint configurations](./lint_configuration.md) -to see what configuration options you can set and the lints they configure. +The [table of configurations](./lint_configuration.md) +contains all config values, their default, and a list of lints they affect. +Each [configurable lint](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration) +, also contains information about these values. For configurations that are a list type with default values such as [disallowed-names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names), diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 102ed7ad2cea..cfaaefe3ea18 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -1,52 +1,518 @@ -## Configuration Options - -| Option | Default | Description | Lints | -|--|--|--|--| -| arithmetic-side-effects-allowed | `{}` | Suppress checking of the passed type names in all types of operations | [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) | -| arithmetic-side-effects-allowed-binary | `[]` | Suppress checking of the passed type pair names in binary operations like addition or multiplication | [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) | -| arithmetic-side-effects-allowed-unary | `{}` | Suppress checking of the passed type names in unary operations like "negation" (`-`) | [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) | -| avoid-breaking-exported-api | `true` | Suppress lints whenever the suggested change would cause breakage for other crates | [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) [unnecessary_wraps](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps) [unused_self](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self) [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) [wrong_self_convention](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention) [box_collection](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection) [redundant_allocation](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation) [rc_buffer](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer) [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) [option_option](https://rust-lang.github.io/rust-clippy/master/index.html#option_option) [linkedlist](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist) [rc_mutex](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex) | -| msrv | `None` | The minimum rust version that the project supports | [manual_split_once](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) [manual_str_repeat](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) [cloned_instead_of_copied](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied) [redundant_field_names](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) [redundant_static_lifetimes](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) [filter_map_next](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next) [checked_conversions](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions) [manual_range_contains](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains) [use_self](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) [mem_replace_with_default](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) [manual_non_exhaustive](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) [option_as_ref_deref](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) [map_unwrap_or](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or) [match_like_matches_macro](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro) [manual_strip](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) [missing_const_for_fn](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) [unnested_or_patterns](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) [from_over_into](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) [ptr_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) [if_then_some_else_none](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) [approx_constant](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant) [deprecated_cfg_attr](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr) [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) [map_clone](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) [borrow_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr) [manual_bits](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) [err_expect](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect) [cast_abs_to_unsigned](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned) [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) | -| cognitive-complexity-threshold | `25` | The maximum cognitive complexity a function can have | [cognitive_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity) | -| disallowed-names | `["foo", "baz", "quux"]` | The list of disallowed names to lint about | [disallowed_names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names) | -| doc-valid-idents | `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` | The list of words this lint should not consider as identifiers needing ticks | [doc_markdown](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown) | -| too-many-arguments-threshold | `7` | The maximum number of argument a function or method can have | [too_many_arguments](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments) | -| type-complexity-threshold | `250` | The maximum complexity a type can have | [type_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity) | -| single-char-binding-names-threshold | `4` | The maximum number of single char bindings a scope may have | [many_single_char_names](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names) | -| too-large-for-stack | `200` | The maximum size of objects (in bytes) that will be linted | [boxed_local](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local) [useless_vec](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec) | -| enum-variant-name-threshold | `3` | The minimum number of enum variants for the lints about variant names to trigger | [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) | -| enum-variant-size-threshold | `200` | The maximum size of an enum's variant to avoid box suggestion | [large_enum_variant](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant) | -| verbose-bit-mask-threshold | `1` | The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' | [verbose_bit_mask](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask) | -| literal-representation-threshold | `16384` | The lower bound for linting decimal literals | [decimal_literal_representation](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation) | -| trivial-copy-size-limit | `None` | The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference | [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) | -| pass-by-value-size-limit | `256` | The minimum size (in bytes) to consider a type for passing by reference instead of by value | [large_type_pass_by_move](https://rust-lang.github.io/rust-clippy/master/index.html#large_type_pass_by_move) | -| too-many-lines-threshold | `100` | The maximum number of lines a function or method can have | [too_many_lines](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) | -| array-size-threshold | `512000` | The maximum allowed size for arrays on the stack | [large_stack_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays) [large_const_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays) | -| vec-box-size-threshold | `4096` | The size of the boxed type in bytes, where boxing in a `Vec` is allowed | [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) | -| max-trait-bounds | `3` | The maximum number of bounds a trait can have to be linted | [type_repetition_in_bounds](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) | -| max-struct-bools | `3` | The maximum number of bool fields a struct can have | [struct_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools) | -| max-fn-params-bools | `3` | The maximum number of bool parameters a function can have | [fn_params_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools) | -| warn-on-all-wildcard-imports | `false` | Whether to allow certain wildcard imports (prelude, super in tests) | [wildcard_imports](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) | -| disallowed-macros | `[]` | The list of disallowed macros, written as fully qualified paths | [disallowed_macros](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros) | -| disallowed-methods | `[]` | The list of disallowed methods, written as fully qualified paths | [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) | -| disallowed-types | `[]` | The list of disallowed types, written as fully qualified paths | [disallowed_types](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) | -| unreadable-literal-lint-fractions | `true` | Should the fraction of a decimal be linted to include separators | [unreadable_literal](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal) | -| upper-case-acronyms-aggressive | `false` | Enables verbose mode | [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) | -| matches-for-let-else | `WellKnownTypes` | Whether the matches should be considered by the lint, and whether there should be filtering for common types | [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) | -| cargo-ignore-publish | `false` | For internal testing only, ignores the current `publish` settings in the Cargo manifest | [_cargo_common_metadata](https://rust-lang.github.io/rust-clippy/master/index.html#_cargo_common_metadata) | -| standard-macro-braces | `[]` | Enforce the named macros always use the braces specified | [nonstandard_macro_braces](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces) | -| enforced-import-renames | `[]` | The list of imports to always rename, a fully qualified path followed by the rename | [missing_enforced_import_renames](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames) | -| allowed-scripts | `["Latin"]` | The list of unicode scripts allowed to be used in the scope | [disallowed_script_idents](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents) | -| enable-raw-pointer-heuristic-for-send | `true` | Whether to apply the raw pointer heuristic to determine if a type is `Send` | [non_send_fields_in_send_ty](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty) | -| max-suggested-slice-pattern-length | `3` | When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in the slice pattern that is suggested | [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) | -| await-holding-invalid-types | `[]` | [ERROR] MALFORMED DOC COMMENT | | -| max-include-file-size | `1000000` | The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes | [large_include_file](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file) | -| allow-expect-in-tests | `false` | Whether `expect` should be allowed within `#[cfg(test)]` | [expect_used](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used) | -| allow-unwrap-in-tests | `false` | Whether `unwrap` should be allowed in test cfg | [unwrap_used](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) | -| allow-dbg-in-tests | `false` | Whether `dbg!` should be allowed in test functions | [dbg_macro](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) | -| allow-print-in-tests | `false` | Whether print macros (ex | [print_stdout](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout) [print_stderr](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr) | -| large-error-threshold | `128` | The maximum size of the `Err`-variant in a `Result` returned from a function | [result_large_err](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) | -| ignore-interior-mutability | `["bytes::Bytes"]` | A list of paths to types that should be treated like `Arc`, i | [mutable_key](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key) | -| allow-mixed-uninlined-format-args | `true` | Whether to allow mixed uninlined format args, e | [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) | -| suppress-restriction-lint-in-const | `false` | In same cases the restructured operation might not be unavoidable, as the suggested counterparts are unavailable in constant code | [indexing_slicing](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing) | +## Lint Configuration Options +|
    Option
    | Default Value | +|--|--| +| [arithmetic-side-effects-allowed](#arithmetic-side-effects-allowed) | `{}` | +| [arithmetic-side-effects-allowed-binary](#arithmetic-side-effects-allowed-binary) | `[]` | +| [arithmetic-side-effects-allowed-unary](#arithmetic-side-effects-allowed-unary) | `{}` | +| [avoid-breaking-exported-api](#avoid-breaking-exported-api) | `true` | +| [msrv](#msrv) | `None` | +| [cognitive-complexity-threshold](#cognitive-complexity-threshold) | `25` | +| [disallowed-names](#disallowed-names) | `["foo", "baz", "quux"]` | +| [doc-valid-idents](#doc-valid-idents) | `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` | +| [too-many-arguments-threshold](#too-many-arguments-threshold) | `7` | +| [type-complexity-threshold](#type-complexity-threshold) | `250` | +| [single-char-binding-names-threshold](#single-char-binding-names-threshold) | `4` | +| [too-large-for-stack](#too-large-for-stack) | `200` | +| [enum-variant-name-threshold](#enum-variant-name-threshold) | `3` | +| [enum-variant-size-threshold](#enum-variant-size-threshold) | `200` | +| [verbose-bit-mask-threshold](#verbose-bit-mask-threshold) | `1` | +| [literal-representation-threshold](#literal-representation-threshold) | `16384` | +| [trivial-copy-size-limit](#trivial-copy-size-limit) | `None` | +| [pass-by-value-size-limit](#pass-by-value-size-limit) | `256` | +| [too-many-lines-threshold](#too-many-lines-threshold) | `100` | +| [array-size-threshold](#array-size-threshold) | `512000` | +| [vec-box-size-threshold](#vec-box-size-threshold) | `4096` | +| [max-trait-bounds](#max-trait-bounds) | `3` | +| [max-struct-bools](#max-struct-bools) | `3` | +| [max-fn-params-bools](#max-fn-params-bools) | `3` | +| [warn-on-all-wildcard-imports](#warn-on-all-wildcard-imports) | `false` | +| [disallowed-macros](#disallowed-macros) | `[]` | +| [disallowed-methods](#disallowed-methods) | `[]` | +| [disallowed-types](#disallowed-types) | `[]` | +| [unreadable-literal-lint-fractions](#unreadable-literal-lint-fractions) | `true` | +| [upper-case-acronyms-aggressive](#upper-case-acronyms-aggressive) | `false` | +| [matches-for-let-else](#matches-for-let-else) | `WellKnownTypes` | +| [cargo-ignore-publish](#cargo-ignore-publish) | `false` | +| [standard-macro-braces](#standard-macro-braces) | `[]` | +| [enforced-import-renames](#enforced-import-renames) | `[]` | +| [allowed-scripts](#allowed-scripts) | `["Latin"]` | +| [enable-raw-pointer-heuristic-for-send](#enable-raw-pointer-heuristic-for-send) | `true` | +| [max-suggested-slice-pattern-length](#max-suggested-slice-pattern-length) | `3` | +| [max-include-file-size](#max-include-file-size) | `1000000` | +| [allow-expect-in-tests](#allow-expect-in-tests) | `false` | +| [allow-unwrap-in-tests](#allow-unwrap-in-tests) | `false` | +| [allow-dbg-in-tests](#allow-dbg-in-tests) | `false` | +| [allow-print-in-tests](#allow-print-in-tests) | `false` | +| [large-error-threshold](#large-error-threshold) | `128` | +| [ignore-interior-mutability](#ignore-interior-mutability) | `["bytes::Bytes"]` | +| [allow-mixed-uninlined-format-args](#allow-mixed-uninlined-format-args) | `true` | +| [suppress-restriction-lint-in-const](#suppress-restriction-lint-in-const) | `false` | + +### arithmetic-side-effects-allowed +Suppress checking of the passed type names in all types of operations. + +If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. + +#### Example + +```toml +arithmetic-side-effects-allowed = ["SomeType", "AnotherType"] +``` + +#### Noteworthy + +A type, say `SomeType`, listed in this configuration has the same behavior of +`["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. + +**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet`) + +* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) + + +### arithmetic-side-effects-allowed-binary +Suppress checking of the passed type pair names in binary operations like addition or +multiplication. + +Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless +of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`. + +Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as +`["AnotherType", "SomeType"]`. + +#### Example + +```toml +arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] +``` + +**Default Value:** `[]` (`Vec<[String; 2]>`) + +* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) + + +### arithmetic-side-effects-allowed-unary +Suppress checking of the passed type names in unary operations like "negation" (`-`). + +#### Example + +```toml +arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] +``` + +**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet`) + +* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) + + +### avoid-breaking-exported-api +Suppress lints whenever the suggested change would cause breakage for other crates. + +**Default Value:** `true` (`bool`) + +* [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) +* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) +* [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) +* [unnecessary_wraps](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps) +* [unused_self](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self) +* [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) +* [wrong_self_convention](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention) +* [box_collection](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection) +* [redundant_allocation](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation) +* [rc_buffer](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer) +* [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) +* [option_option](https://rust-lang.github.io/rust-clippy/master/index.html#option_option) +* [linkedlist](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist) +* [rc_mutex](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex) + + +### msrv +The minimum rust version that the project supports + +**Default Value:** `None` (`Option`) + +* [manual_split_once](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) +* [manual_str_repeat](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) +* [cloned_instead_of_copied](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied) +* [redundant_field_names](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) +* [redundant_static_lifetimes](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) +* [filter_map_next](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next) +* [checked_conversions](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions) +* [manual_range_contains](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains) +* [use_self](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) +* [mem_replace_with_default](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) +* [manual_non_exhaustive](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) +* [option_as_ref_deref](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) +* [map_unwrap_or](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or) +* [match_like_matches_macro](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro) +* [manual_strip](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) +* [missing_const_for_fn](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) +* [unnested_or_patterns](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) +* [from_over_into](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) +* [ptr_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) +* [if_then_some_else_none](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) +* [approx_constant](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant) +* [deprecated_cfg_attr](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr) +* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +* [map_clone](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) +* [borrow_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr) +* [manual_bits](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) +* [err_expect](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect) +* [cast_abs_to_unsigned](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned) +* [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) +* [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) +* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) +* [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) + + +### cognitive-complexity-threshold +The maximum cognitive complexity a function can have + +**Default Value:** `25` (`u64`) + +* [cognitive_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity) + + +### disallowed-names +The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value +`".."` can be used as part of the list to indicate, that the configured values should be appended to the +default configuration of Clippy. By default any configuration will replace the default value. + +**Default Value:** `["foo", "baz", "quux"]` (`Vec`) + +* [disallowed_names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names) + + +### doc-valid-idents +The list of words this lint should not consider as identifiers needing ticks. The value +`".."` can be used as part of the list to indicate, that the configured values should be appended to the +default configuration of Clippy. By default any configuraction will replace the default value. For example: +* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. +* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. + +Default list: + +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` (`Vec`) + +* [doc_markdown](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown) + + +### too-many-arguments-threshold +The maximum number of argument a function or method can have + +**Default Value:** `7` (`u64`) + +* [too_many_arguments](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments) + + +### type-complexity-threshold +The maximum complexity a type can have + +**Default Value:** `250` (`u64`) + +* [type_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity) + + +### single-char-binding-names-threshold +The maximum number of single char bindings a scope may have + +**Default Value:** `4` (`u64`) + +* [many_single_char_names](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names) + + +### too-large-for-stack +The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + +**Default Value:** `200` (`u64`) + +* [boxed_local](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local) +* [useless_vec](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec) + + +### enum-variant-name-threshold +The minimum number of enum variants for the lints about variant names to trigger + +**Default Value:** `3` (`u64`) + +* [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) + + +### enum-variant-size-threshold +The maximum size of an enum's variant to avoid box suggestion + +**Default Value:** `200` (`u64`) + +* [large_enum_variant](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant) + + +### verbose-bit-mask-threshold +The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' + +**Default Value:** `1` (`u64`) + +* [verbose_bit_mask](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask) + + +### literal-representation-threshold +The lower bound for linting decimal literals + +**Default Value:** `16384` (`u64`) + +* [decimal_literal_representation](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation) + + +### trivial-copy-size-limit +The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. + +**Default Value:** `None` (`Option`) + +* [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) + + +### pass-by-value-size-limit +The minimum size (in bytes) to consider a type for passing by reference instead of by value. + +**Default Value:** `256` (`u64`) + +* [large_type_pass_by_move](https://rust-lang.github.io/rust-clippy/master/index.html#large_type_pass_by_move) + + +### too-many-lines-threshold +The maximum number of lines a function or method can have + +**Default Value:** `100` (`u64`) + +* [too_many_lines](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) + + +### array-size-threshold +The maximum allowed size for arrays on the stack + +**Default Value:** `512000` (`u128`) + +* [large_stack_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays) +* [large_const_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays) + + +### vec-box-size-threshold +The size of the boxed type in bytes, where boxing in a `Vec` is allowed + +**Default Value:** `4096` (`u64`) + +* [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) + + +### max-trait-bounds +The maximum number of bounds a trait can have to be linted + +**Default Value:** `3` (`u64`) + +* [type_repetition_in_bounds](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) + + +### max-struct-bools +The maximum number of bool fields a struct can have + +**Default Value:** `3` (`u64`) + +* [struct_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools) + + +### max-fn-params-bools +The maximum number of bool parameters a function can have + +**Default Value:** `3` (`u64`) + +* [fn_params_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools) + + +### warn-on-all-wildcard-imports +Whether to allow certain wildcard imports (prelude, super in tests). + +**Default Value:** `false` (`bool`) + +* [wildcard_imports](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) + + +### disallowed-macros +The list of disallowed macros, written as fully qualified paths. + +**Default Value:** `[]` (`Vec`) + +* [disallowed_macros](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros) + + +### disallowed-methods +The list of disallowed methods, written as fully qualified paths. + +**Default Value:** `[]` (`Vec`) + +* [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) + + +### disallowed-types +The list of disallowed types, written as fully qualified paths. + +**Default Value:** `[]` (`Vec`) + +* [disallowed_types](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) + + +### unreadable-literal-lint-fractions +Should the fraction of a decimal be linted to include separators. + +**Default Value:** `true` (`bool`) + +* [unreadable_literal](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal) + + +### upper-case-acronyms-aggressive +Enables verbose mode. Triggers if there is more than one uppercase char next to each other + +**Default Value:** `false` (`bool`) + +* [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) + + +### matches-for-let-else +Whether the matches should be considered by the lint, and whether there should +be filtering for common types. + +**Default Value:** `WellKnownTypes` (`crate::manual_let_else::MatchLintBehaviour`) + +* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) + + +### cargo-ignore-publish +For internal testing only, ignores the current `publish` settings in the Cargo manifest. + +**Default Value:** `false` (`bool`) + +* [_cargo_common_metadata](https://rust-lang.github.io/rust-clippy/master/index.html#_cargo_common_metadata) + + +### standard-macro-braces +Enforce the named macros always use the braces specified. + +A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro +is could be used with a full path two `MacroMatcher`s have to be added one with the full path +`crate_name::macro_name` and one with just the macro name. + +**Default Value:** `[]` (`Vec`) + +* [nonstandard_macro_braces](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces) + + +### enforced-import-renames +The list of imports to always rename, a fully qualified path followed by the rename. + +**Default Value:** `[]` (`Vec`) + +* [missing_enforced_import_renames](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames) + + +### allowed-scripts +The list of unicode scripts allowed to be used in the scope. + +**Default Value:** `["Latin"]` (`Vec`) + +* [disallowed_script_idents](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents) + + +### enable-raw-pointer-heuristic-for-send +Whether to apply the raw pointer heuristic to determine if a type is `Send`. + +**Default Value:** `true` (`bool`) + +* [non_send_fields_in_send_ty](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty) + + +### max-suggested-slice-pattern-length +When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in +the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. +For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. + +**Default Value:** `3` (`u64`) + +* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) + + +### max-include-file-size +The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes + +**Default Value:** `1000000` (`u64`) + +* [large_include_file](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file) + + +### allow-expect-in-tests +Whether `expect` should be allowed within `#[cfg(test)]` + +**Default Value:** `false` (`bool`) + +* [expect_used](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used) + + +### allow-unwrap-in-tests +Whether `unwrap` should be allowed in test cfg + +**Default Value:** `false` (`bool`) + +* [unwrap_used](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) + + +### allow-dbg-in-tests +Whether `dbg!` should be allowed in test functions + +**Default Value:** `false` (`bool`) + +* [dbg_macro](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) + + +### allow-print-in-tests +Whether print macros (ex. `println!`) should be allowed in test functions + +**Default Value:** `false` (`bool`) + +* [print_stdout](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout) +* [print_stderr](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr) + + +### large-error-threshold +The maximum size of the `Err`-variant in a `Result` returned from a function + +**Default Value:** `128` (`u64`) + +* [result_large_err](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) + + +### ignore-interior-mutability +A list of paths to types that should be treated like `Arc`, i.e. ignored but +for the generic parameters for determining interior mutability + +**Default Value:** `["bytes::Bytes"]` (`Vec`) + +* [mutable_key](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key) + + +### allow-mixed-uninlined-format-args +Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` + +**Default Value:** `true` (`bool`) + +* [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) + + +### suppress-restriction-lint-in-const +In same +cases the restructured operation might not be unavoidable, as the +suggested counterparts are unavailable in constant code. This +configuration will cause restriction lints to trigger even +if no suggestion can be made. + +**Default Value:** `false` (`bool`) + +* [indexing_slicing](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing) + + diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index c1589c771c46..f48be27592b7 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -219,7 +219,8 @@ define_Conf! { /// /// #### Noteworthy /// - /// A type, say `SomeType`, listed in this configuration has the same behavior of `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. + /// A type, say `SomeType`, listed in this configuration has the same behavior of + /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. (arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet = <_>::default()), /// Lint: ARITHMETIC_SIDE_EFFECTS. /// diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c74f3b6e3ad6..47604f6933b4 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -180,13 +180,22 @@ This lint has the following configuration variables: }) } - fn get_markdown_table(&self) -> String { + fn configs_to_markdown(&self, map_fn: fn(&ClippyConfiguration) -> String) -> String { self.config .iter() .filter(|config| config.deprecation_reason.is_none()) - .map(ClippyConfiguration::to_markdown_table_entry) + .filter(|config| !config.lints.is_empty()) + .map(map_fn) .join("\n") } + + fn get_markdown_docs(&self) -> String { + format!( + "## Lint Configuration Options\n|
    Option
    | Default Value |\n|--|--|\n{}\n\n{}\n", + self.configs_to_markdown(ClippyConfiguration::to_markdown_table_entry), + self.configs_to_markdown(ClippyConfiguration::to_markdown_paragraph), + ) + } } impl Drop for MetadataCollector { @@ -230,12 +239,7 @@ impl Drop for MetadataCollector { .create(true) .open(MARKDOWN_OUTPUT_FILE) .unwrap(); - writeln!( - file, - "## Lint Configuration\n\n| Option | Default | Description | Lints |\n|--|--|--|--|\n{}\n", - self.get_markdown_table() - ) - .unwrap(); + writeln!(file, "{}", self.get_markdown_docs(),).unwrap(); } } @@ -537,24 +541,27 @@ impl ClippyConfiguration { } } - fn to_markdown_table_entry(&self) -> String { + fn to_markdown_paragraph(&self) -> String { format!( - "| {} | `{}` | {} | {} |", + "### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n", self.name, - self.default, self.doc - .split('.') - .next() - .unwrap_or("") - .replace('|', "\\|") - .replace("\n ", " "), + .lines() + .map(|line| line.strip_prefix(" ").unwrap_or(line)) + .join("\n"), + self.default, + self.config_type, self.lints .iter() .map(|name| name.to_string().split_whitespace().next().unwrap().to_string()) - .map(|name| format!("[{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) - .join(" ") + .map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) + .join("\n"), ) } + + fn to_markdown_table_entry(&self) -> String { + format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default) + } } fn collect_configs() -> Vec { From a7db92574cf191444f5bbfdfcb89af5655e280c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maria=20Jos=C3=A9=20Solano?= Date: Fri, 13 Jan 2023 18:57:04 -0800 Subject: [PATCH 464/524] Split long line --- book/src/development/adding_lints.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 145b393b7535..c6be394bd7d7 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -146,7 +146,8 @@ For cargo lints, the process of testing differs in that we are interested in the manifest. If our new lint is named e.g. `foo_categories`, after running `cargo dev -new_lint --name=foo_categories --type=cargo --category=cargo` we will find by default two new crates, each with its manifest file: +new_lint --name=foo_categories --type=cargo --category=cargo` we will find by +default two new crates, each with its manifest file: * `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. From a160ce3a48c626bc30894ca50a2b1024c8f1d672 Mon Sep 17 00:00:00 2001 From: Kyle Matsuda Date: Tue, 10 Jan 2023 14:22:52 -0700 Subject: [PATCH 465/524] change usages of impl_trait_ref to bound_impl_trait_ref --- clippy_lints/src/derive.rs | 8 ++++---- clippy_lints/src/fallible_impl_from.rs | 4 ++-- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/implicit_saturating_sub.rs | 4 ++-- clippy_lints/src/methods/implicit_clone.rs | 2 +- clippy_lints/src/methods/suspicious_splitn.rs | 2 +- clippy_lints/src/missing_doc.rs | 2 +- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/non_send_fields_in_send_ty.rs | 4 ++-- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/use_self.rs | 4 ++-- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index f4b15e0916db..6b2b18fff76e 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -247,11 +247,11 @@ fn check_hash_peq<'tcx>( return; } - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + let trait_ref = cx.tcx.bound_impl_trait_ref(impl_id).expect("must be a trait implementation"); // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] - if trait_ref.substs.type_at(1) == ty { + if trait_ref.subst_identity().substs.type_at(1) == ty { span_lint_and_then( cx, DERIVED_HASH_WITH_MANUAL_EQ, @@ -295,11 +295,11 @@ fn check_ord_partial_ord<'tcx>( return; } - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + let trait_ref = cx.tcx.bound_impl_trait_ref(impl_id).expect("must be a trait implementation"); // Only care about `impl PartialOrd for Foo` // For `impl PartialOrd for A, input_types is [A, B] - if trait_ref.substs.type_at(1) == ty { + if trait_ref.subst_identity().substs.type_at(1) == ty { let mess = if partial_ord_is_automatically_derived { "you are implementing `Ord` explicitly but have derived `PartialOrd`" } else { diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 9a1058470e18..a8085122ccf9 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -55,8 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { // check for `impl From for ..` if_chain! { if let hir::ItemKind::Impl(impl_) = &item.kind; - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); - if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.def_id); + if let Some(impl_trait_ref) = cx.tcx.bound_impl_trait_ref(item.owner_id.to_def_id()); + if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id); then { lint_impl_body(cx, item.span, impl_.items); } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index a92f7548ff25..97d414bfa95e 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() // `impl Into for self_ty` && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args - && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) + && let Some(middle_trait_ref) = cx.tcx.bound_impl_trait_ref(item.owner_id.to_def_id()).map(ty::EarlyBinder::subst_identity) && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) && !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Alias(ty::Opaque, _)) { diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 29d59c26d92c..37e33529a9a6 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -101,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if name.ident.as_str() == "MIN"; if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(const_id); - if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl + if let None = cx.tcx.bound_impl_trait_ref(impl_id); // An inherent impl if cx.tcx.type_of(impl_id).is_integral(); then { print_lint_and_sugg(cx, var_name, expr) @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if name.ident.as_str() == "min_value"; if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(func_id); - if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl + if let None = cx.tcx.bound_impl_trait_ref(impl_id); // An inherent impl if cx.tcx.type_of(impl_id).is_integral(); then { print_lint_and_sugg(cx, var_name, expr) diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 06ecbce4e70e..16a25a98800d 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -53,7 +53,7 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir "to_vec" => cx .tcx .impl_of_method(method_def_id) - .filter(|&impl_did| cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none()) + .filter(|&impl_did| cx.tcx.type_of(impl_did).is_slice() && cx.tcx.bound_impl_trait_ref(impl_did).is_none()) .is_some(), _ => false, } diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index 219a9edd6576..dba0663467b7 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se if count <= 1; if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(call_id); - if cx.tcx.impl_trait_ref(impl_id).is_none(); + if cx.tcx.bound_impl_trait_ref(impl_id).is_none(); let self_ty = cx.tcx.type_of(impl_id); if self_ty.is_slice() || self_ty.is_str(); then { diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 6fd100762b49..d0c99b352452 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -175,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { // If the method is an impl for a trait, don't doc. if let Some(cid) = cx.tcx.associated_item(impl_item.owner_id).impl_container(cx.tcx) { - if cx.tcx.impl_trait_ref(cid).is_some() { + if cx.tcx.bound_impl_trait_ref(cid).is_some() { return; } } else { diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 758ce47cf114..0594fb175458 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -155,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { TraitContainer => Some(container_id), - ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.def_id), + ImplContainer => cx.tcx.bound_impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), }; if let Some(trait_def_id) = trait_def_id { diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs index 714c0ff227bf..9c112ade948f 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -89,8 +89,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { if let Some(trait_id) = trait_ref.trait_def_id(); if send_trait == trait_id; if hir_impl.polarity == ImplPolarity::Positive; - if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); - if let self_ty = ty_trait_ref.self_ty(); + if let Some(ty_trait_ref) = cx.tcx.bound_impl_trait_ref(item.owner_id.to_def_id()); + if let self_ty = ty_trait_ref.subst_identity().self_ty(); if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind(); then { let mut non_send_fields = Vec::new(); diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 7722a476d7b4..82b1716a216e 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -244,7 +244,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { })) => { #[allow(trivial_casts)] if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into()) - && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) + && let Some(trait_ref) = cx.tcx.bound_impl_trait_ref(item.owner_id.to_def_id()).map(|t| t.subst_identity()) && let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id { ( diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 4c755d812a0e..9f31a13aa984 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -133,11 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { ref mut types_to_skip, .. }) = self.stack.last_mut(); - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id); + if let Some(impl_trait_ref) = cx.tcx.bound_impl_trait_ref(impl_id.to_def_id()); then { // `self_ty` is the semantic self type of `impl for `. This cannot be // `Self`. - let self_ty = impl_trait_ref.self_ty(); + let self_ty = impl_trait_ref.subst_identity().self_ty(); // `trait_method_sig` is the signature of the function, how it is declared in the // trait, not in the impl of the trait. From b92d90211e9d4ac8275912d6a913969d6a9b7913 Mon Sep 17 00:00:00 2001 From: Kyle Matsuda Date: Tue, 10 Jan 2023 14:57:22 -0700 Subject: [PATCH 466/524] change impl_trait_ref query to return EarlyBinder; remove bound_impl_trait_ref query; add EarlyBinder to impl_trait_ref in metadata --- clippy_lints/src/derive.rs | 4 ++-- clippy_lints/src/fallible_impl_from.rs | 2 +- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/implicit_saturating_sub.rs | 4 ++-- clippy_lints/src/methods/implicit_clone.rs | 2 +- clippy_lints/src/methods/suspicious_splitn.rs | 2 +- clippy_lints/src/missing_doc.rs | 2 +- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/non_send_fields_in_send_ty.rs | 2 +- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/use_self.rs | 2 +- 11 files changed, 13 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 6b2b18fff76e..248d73884106 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -247,7 +247,7 @@ fn check_hash_peq<'tcx>( return; } - let trait_ref = cx.tcx.bound_impl_trait_ref(impl_id).expect("must be a trait implementation"); + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] @@ -295,7 +295,7 @@ fn check_ord_partial_ord<'tcx>( return; } - let trait_ref = cx.tcx.bound_impl_trait_ref(impl_id).expect("must be a trait implementation"); + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); // Only care about `impl PartialOrd for Foo` // For `impl PartialOrd for A, input_types is [A, B] diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index a8085122ccf9..2ef547526d4f 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { // check for `impl From for ..` if_chain! { if let hir::ItemKind::Impl(impl_) = &item.kind; - if let Some(impl_trait_ref) = cx.tcx.bound_impl_trait_ref(item.owner_id.to_def_id()); + if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id); then { lint_impl_body(cx, item.span, impl_.items); diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 97d414bfa95e..bd66ace4500a 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() // `impl Into for self_ty` && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args - && let Some(middle_trait_ref) = cx.tcx.bound_impl_trait_ref(item.owner_id.to_def_id()).map(ty::EarlyBinder::subst_identity) + && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(ty::EarlyBinder::subst_identity) && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) && !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Alias(ty::Opaque, _)) { diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 37e33529a9a6..29d59c26d92c 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -101,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if name.ident.as_str() == "MIN"; if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(const_id); - if let None = cx.tcx.bound_impl_trait_ref(impl_id); // An inherent impl + if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl if cx.tcx.type_of(impl_id).is_integral(); then { print_lint_and_sugg(cx, var_name, expr) @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if name.ident.as_str() == "min_value"; if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(func_id); - if let None = cx.tcx.bound_impl_trait_ref(impl_id); // An inherent impl + if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl if cx.tcx.type_of(impl_id).is_integral(); then { print_lint_and_sugg(cx, var_name, expr) diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 16a25a98800d..06ecbce4e70e 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -53,7 +53,7 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir "to_vec" => cx .tcx .impl_of_method(method_def_id) - .filter(|&impl_did| cx.tcx.type_of(impl_did).is_slice() && cx.tcx.bound_impl_trait_ref(impl_did).is_none()) + .filter(|&impl_did| cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none()) .is_some(), _ => false, } diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index dba0663467b7..219a9edd6576 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se if count <= 1; if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(call_id); - if cx.tcx.bound_impl_trait_ref(impl_id).is_none(); + if cx.tcx.impl_trait_ref(impl_id).is_none(); let self_ty = cx.tcx.type_of(impl_id); if self_ty.is_slice() || self_ty.is_str(); then { diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index d0c99b352452..6fd100762b49 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -175,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { // If the method is an impl for a trait, don't doc. if let Some(cid) = cx.tcx.associated_item(impl_item.owner_id).impl_container(cx.tcx) { - if cx.tcx.bound_impl_trait_ref(cid).is_some() { + if cx.tcx.impl_trait_ref(cid).is_some() { return; } } else { diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 0594fb175458..5a459548153a 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -155,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { TraitContainer => Some(container_id), - ImplContainer => cx.tcx.bound_impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), }; if let Some(trait_def_id) = trait_def_id { diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs index 9c112ade948f..839c3a3815c2 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -89,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { if let Some(trait_id) = trait_ref.trait_def_id(); if send_trait == trait_id; if hir_impl.polarity == ImplPolarity::Positive; - if let Some(ty_trait_ref) = cx.tcx.bound_impl_trait_ref(item.owner_id.to_def_id()); + if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); if let self_ty = ty_trait_ref.subst_identity().self_ty(); if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind(); then { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 82b1716a216e..7b1d974f2f87 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -244,7 +244,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { })) => { #[allow(trivial_casts)] if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into()) - && let Some(trait_ref) = cx.tcx.bound_impl_trait_ref(item.owner_id.to_def_id()).map(|t| t.subst_identity()) + && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(|t| t.subst_identity()) && let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id { ( diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 9f31a13aa984..6ae9d9d63538 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { ref mut types_to_skip, .. }) = self.stack.last_mut(); - if let Some(impl_trait_ref) = cx.tcx.bound_impl_trait_ref(impl_id.to_def_id()); + if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id); then { // `self_ty` is the semantic self type of `impl for `. This cannot be // `Self`. From dc5ce488e7e14e2cebb90d9c1cd189c970ee415f Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Sat, 14 Jan 2023 07:38:29 -0700 Subject: [PATCH 467/524] Move CI tests for collect-metadata to clippy_bors.yml --- .github/workflows/clippy_bors.yml | 5 +++++ .github/workflows/remark.yml | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 1bc457a94793..24e677ce8e17 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -157,6 +157,11 @@ jobs: - name: Test metadata collection run: cargo collect-metadata + - name: Test lint_configuration.md is up-to-date + run: | + echo "run \`cargo collect-metadata\` if this fails" + git update-index --refresh + integration_build: needs: changelog runs-on: ubuntu-latest diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 22925a753dfe..81ef072bbb07 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -33,9 +33,6 @@ jobs: echo `pwd`/mdbook >> $GITHUB_PATH # Run - - name: cargo collect-metadata - run: cargo collect-metadata - - name: Check *.md files run: git ls-files -z '*.md' | xargs -0 -n 1 -I {} ./node_modules/.bin/remark {} -u lint -f > /dev/null From d950279a039b5a598c87b347ba5d2f151a071a98 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Sat, 14 Jan 2023 07:39:49 -0700 Subject: [PATCH 468/524] Document generating lint config docs for adding configuration --- book/src/development/adding_lints.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 8b4eee8c9d94..fd6e1f5aef20 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -699,6 +699,10 @@ for some users. Adding a configuration is done in the following steps: `clippy.toml` file with the configuration value and a rust file that should be linted by Clippy. The test can otherwise be written as usual. +5. Update [Lint Configuration](../lint_configuration.md) + + Run `cargo collect-metadata` to generate documentation changes for the book. + [`clippy_lints::utils::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/conf.rs [`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs [`tests/ui`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui From c0da8acb72529fdfc156b006e9ee5d2f18f1a730 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Sat, 14 Jan 2023 11:10:40 -0700 Subject: [PATCH 469/524] Comment that lint_configuration.md is machine generated --- book/src/lint_configuration.md | 5 +++++ .../src/utils/internal_lints/metadata_collector.rs | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index cfaaefe3ea18..f79dbb50ff49 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -1,3 +1,8 @@ + + ## Lint Configuration Options |
    Option
    | Default Value | |--|--| diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 47604f6933b4..1995c373835b 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -239,7 +239,17 @@ impl Drop for MetadataCollector { .create(true) .open(MARKDOWN_OUTPUT_FILE) .unwrap(); - writeln!(file, "{}", self.get_markdown_docs(),).unwrap(); + writeln!( + file, + " + +{}", + self.get_markdown_docs(), + ) + .unwrap(); } } From 5dac27503f17181cb0fe71084d95825cfe706504 Mon Sep 17 00:00:00 2001 From: Kyle Matsuda Date: Mon, 16 Jan 2023 14:50:11 -0700 Subject: [PATCH 470/524] change usages of item_bounds query to bound_item_bounds --- clippy_utils/src/ty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index c8d56a3be5cf..9525c78312b4 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -648,7 +648,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))), ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { - sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)) + sig_from_bounds(cx, ty, cx.tcx.bound_item_bounds(def_id).subst_identity(), cx.tcx.opt_parent(def_id)) }, ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), ty::Dynamic(bounds, _, _) => { From a084d7908c1ff4489d5aa3100b83b85aebc67367 Mon Sep 17 00:00:00 2001 From: Kyle Matsuda Date: Mon, 16 Jan 2023 15:07:23 -0700 Subject: [PATCH 471/524] change item_bounds query to return EarlyBinder; remove bound_item_bounds query --- clippy_utils/src/ty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 9525c78312b4..1d2a469ca6ca 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -648,7 +648,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))), ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { - sig_from_bounds(cx, ty, cx.tcx.bound_item_bounds(def_id).subst_identity(), cx.tcx.opt_parent(def_id)) + sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id).subst_identity(), cx.tcx.opt_parent(def_id)) }, ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), ty::Dynamic(bounds, _, _) => { From 2bfba8685d12e6cd22a62e32e5cbf370e2b0f516 Mon Sep 17 00:00:00 2001 From: Kyle Matsuda Date: Tue, 17 Jan 2023 08:54:07 -0700 Subject: [PATCH 472/524] fix missing subst in clippy utils --- clippy_utils/src/ty.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 1d2a469ca6ca..99fba4fe741a 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -647,8 +647,8 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))), - ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { - sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id).subst_identity(), cx.tcx.opt_parent(def_id)) + ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { + sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id).subst(cx.tcx, substs), cx.tcx.opt_parent(def_id)) }, ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), ty::Dynamic(bounds, _, _) => { From 875e36f7e459776d3e51b7c089ab89b0821b6122 Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Sun, 8 Jan 2023 06:52:22 +0300 Subject: [PATCH 473/524] Add `multiple_unsafe_ops_per_block` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/multiple_unsafe_ops_per_block.rs | 185 ++++++++++++++++++ tests/ui/multiple_unsafe_ops_per_block.rs | 110 +++++++++++ tests/ui/multiple_unsafe_ops_per_block.stderr | 129 ++++++++++++ 6 files changed, 428 insertions(+) create mode 100644 clippy_lints/src/multiple_unsafe_ops_per_block.rs create mode 100644 tests/ui/multiple_unsafe_ops_per_block.rs create mode 100644 tests/ui/multiple_unsafe_ops_per_block.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e31e8f0d981..84f4654f34e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4383,6 +4383,7 @@ Released 2018-09-13 [`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments [`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions [`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl +[`multiple_unsafe_ops_per_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_unsafe_ops_per_block [`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate [`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit [`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 91ca73633f06..36a366fc9747 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -422,6 +422,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::module_style::MOD_MODULE_FILES_INFO, crate::module_style::SELF_NAMED_MODULE_FILES_INFO, crate::multi_assignments::MULTI_ASSIGNMENTS_INFO, + crate::multiple_unsafe_ops_per_block::MULTIPLE_UNSAFE_OPS_PER_BLOCK_INFO, crate::mut_key::MUTABLE_KEY_TYPE_INFO, crate::mut_mut::MUT_MUT_INFO, crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d8e2ae02c5a6..5c4b60410441 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -198,6 +198,7 @@ mod missing_trait_methods; mod mixed_read_write_in_expression; mod module_style; mod multi_assignments; +mod multiple_unsafe_ops_per_block; mod mut_key; mod mut_mut; mod mut_reference; @@ -908,6 +909,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck)); store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); + store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs new file mode 100644 index 000000000000..18e61c75eece --- /dev/null +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -0,0 +1,185 @@ +use clippy_utils::{ + diagnostics::span_lint_and_then, + visitors::{for_each_expr_with_closures, Descend, Visitable}, +}; +use core::ops::ControlFlow::Continue; +use hir::{ + def::{DefKind, Res}, + BlockCheckMode, ExprKind, QPath, UnOp, Unsafety, +}; +use rustc_ast::Mutability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `unsafe` blocks that contain more than one unsafe operation. + /// + /// ### Why is this bad? + /// Combined with `undocumented_unsafe_blocks`, + /// this lint ensures that each unsafe operation must be independently justified. + /// Combined with `unused_unsafe`, this lint also ensures + /// elimination of unnecessary unsafe blocks through refactoring. + /// + /// ### Example + /// ```rust + /// /// Reads a `char` from the given pointer. + /// /// + /// /// # Safety + /// /// + /// /// `ptr` must point to four consecutive, initialized bytes which + /// /// form a valid `char` when interpreted in the native byte order. + /// fn read_char(ptr: *const u8) -> char { + /// // SAFETY: The caller has guaranteed that the value pointed + /// // to by `bytes` is a valid `char`. + /// unsafe { char::from_u32_unchecked(*ptr.cast::()) } + /// } + /// ``` + /// Use instead: + /// ```rust + /// /// Reads a `char` from the given pointer. + /// /// + /// /// # Safety + /// /// + /// /// - `ptr` must be 4-byte aligned, point to four consecutive + /// /// initialized bytes, and be valid for reads of 4 bytes. + /// /// - The bytes pointed to by `ptr` must represent a valid + /// /// `char` when interpreted in the native byte order. + /// fn read_char(ptr: *const u8) -> char { + /// // SAFETY: `ptr` is 4-byte aligned, points to four consecutive + /// // initialized bytes, and is valid for reads of 4 bytes. + /// let int_value = unsafe { *ptr.cast::() }; + /// + /// // SAFETY: The caller has guaranteed that the four bytes + /// // pointed to by `bytes` represent a valid `char`. + /// unsafe { char::from_u32_unchecked(int_value) } + /// } + /// ``` + #[clippy::version = "1.68.0"] + pub MULTIPLE_UNSAFE_OPS_PER_BLOCK, + restriction, + "more than one unsafe operation per `unsafe` block" +} +declare_lint_pass!(MultipleUnsafeOpsPerBlock => [MULTIPLE_UNSAFE_OPS_PER_BLOCK]); + +impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) { + return; + } + let mut unsafe_ops = vec![]; + collect_unsafe_exprs(cx, block, &mut unsafe_ops); + if unsafe_ops.len() > 1 { + span_lint_and_then( + cx, + MULTIPLE_UNSAFE_OPS_PER_BLOCK, + block.span, + &format!( + "this `unsafe` block contains {} unsafe operations, expected only one", + unsafe_ops.len() + ), + |diag| { + for (msg, span) in unsafe_ops { + diag.span_note(span, msg); + } + }, + ); + } + } +} + +fn collect_unsafe_exprs<'tcx>( + cx: &LateContext<'tcx>, + node: impl Visitable<'tcx>, + unsafe_ops: &mut Vec<(&'static str, Span)>, +) { + for_each_expr_with_closures(cx, node, |expr| { + match expr.kind { + ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)), + + ExprKind::Field(e, _) => { + if cx.typeck_results().expr_ty(e).is_union() { + unsafe_ops.push(("union field access occurs here", expr.span)); + } + }, + + ExprKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::Def(DefKind::Static(Mutability::Mut), _), + .. + }, + )) => { + unsafe_ops.push(("access of a mutable static occurs here", expr.span)); + }, + + ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty_adjusted(e).is_unsafe_ptr() => { + unsafe_ops.push(("raw pointer dereference occurs here", expr.span)); + }, + + ExprKind::Call(path_expr, _) => match path_expr.kind { + ExprKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::Def(kind, def_id), + .. + }, + )) if kind.is_fn_like() => { + let sig = cx.tcx.bound_fn_sig(*def_id); + if sig.0.unsafety() == Unsafety::Unsafe { + unsafe_ops.push(("unsafe function call occurs here", expr.span)); + } + }, + + ExprKind::Path(QPath::TypeRelative(..)) => { + if let Some(sig) = cx + .typeck_results() + .type_dependent_def_id(path_expr.hir_id) + .map(|def_id| cx.tcx.bound_fn_sig(def_id)) + { + if sig.0.unsafety() == Unsafety::Unsafe { + unsafe_ops.push(("unsafe function call occurs here", expr.span)); + } + } + }, + + _ => {}, + }, + + ExprKind::MethodCall(..) => { + if let Some(sig) = cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .map(|def_id| cx.tcx.bound_fn_sig(def_id)) + { + if sig.0.unsafety() == Unsafety::Unsafe { + unsafe_ops.push(("unsafe method call occurs here", expr.span)); + } + } + }, + + ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => { + if matches!( + lhs.kind, + ExprKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::Def(DefKind::Static(Mutability::Mut), _), + .. + } + )) + ) { + unsafe_ops.push(("modification of a mutable static occurs here", expr.span)); + collect_unsafe_exprs(cx, rhs, unsafe_ops); + return Continue(Descend::No); + } + }, + + _ => {}, + }; + + Continue::<(), _>(Descend::Yes) + }); +} diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs new file mode 100644 index 000000000000..41263535df67 --- /dev/null +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -0,0 +1,110 @@ +#![allow(unused)] +#![allow(deref_nullptr)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::drop_copy)] +#![warn(clippy::multiple_unsafe_ops_per_block)] + +use core::arch::asm; + +fn raw_ptr() -> *const () { + core::ptr::null() +} + +unsafe fn not_very_safe() {} + +struct Sample; + +impl Sample { + unsafe fn not_very_safe(&self) {} +} + +#[allow(non_upper_case_globals)] +const sample: Sample = Sample; + +union U { + i: i32, + u: u32, +} + +static mut STATIC: i32 = 0; + +fn test1() { + unsafe { + STATIC += 1; + not_very_safe(); + } +} + +fn test2() { + let u = U { i: 0 }; + + unsafe { + drop(u.u); + *raw_ptr(); + } +} + +fn test3() { + unsafe { + asm!("nop"); + sample.not_very_safe(); + STATIC = 0; + } +} + +fn test_all() { + let u = U { i: 0 }; + unsafe { + drop(u.u); + drop(STATIC); + sample.not_very_safe(); + not_very_safe(); + *raw_ptr(); + asm!("nop"); + } +} + +// no lint +fn correct1() { + unsafe { + STATIC += 1; + } +} + +// no lint +fn correct2() { + unsafe { + STATIC += 1; + } + + unsafe { + *raw_ptr(); + } +} + +// no lint +fn correct3() { + let u = U { u: 0 }; + + unsafe { + not_very_safe(); + } + + unsafe { + drop(u.i); + } +} + +// tests from the issue (https://github.com/rust-lang/rust-clippy/issues/10064) + +unsafe fn read_char_bad(ptr: *const u8) -> char { + unsafe { char::from_u32_unchecked(*ptr.cast::()) } +} + +// no lint +unsafe fn read_char_good(ptr: *const u8) -> char { + let int_value = unsafe { *ptr.cast::() }; + unsafe { core::char::from_u32_unchecked(int_value) } +} + +fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr new file mode 100644 index 000000000000..f6b8341795d2 --- /dev/null +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -0,0 +1,129 @@ +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:32:5 + | +LL | / unsafe { +LL | | STATIC += 1; +LL | | not_very_safe(); +LL | | } + | |_____^ + | +note: modification of a mutable static occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:33:9 + | +LL | STATIC += 1; + | ^^^^^^^^^^^ +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:34:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ + = note: `-D clippy::multiple-unsafe-ops-per-block` implied by `-D warnings` + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:41:5 + | +LL | / unsafe { +LL | | drop(u.u); +LL | | *raw_ptr(); +LL | | } + | |_____^ + | +note: union field access occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:42:14 + | +LL | drop(u.u); + | ^^^ +note: raw pointer dereference occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:43:9 + | +LL | *raw_ptr(); + | ^^^^^^^^^^ + +error: this `unsafe` block contains 3 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:48:5 + | +LL | / unsafe { +LL | | asm!("nop"); +LL | | sample.not_very_safe(); +LL | | STATIC = 0; +LL | | } + | |_____^ + | +note: inline assembly used here + --> $DIR/multiple_unsafe_ops_per_block.rs:49:9 + | +LL | asm!("nop"); + | ^^^^^^^^^^^ +note: unsafe method call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:50:9 + | +LL | sample.not_very_safe(); + | ^^^^^^^^^^^^^^^^^^^^^^ +note: modification of a mutable static occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:51:9 + | +LL | STATIC = 0; + | ^^^^^^^^^^ + +error: this `unsafe` block contains 6 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:57:5 + | +LL | / unsafe { +LL | | drop(u.u); +LL | | drop(STATIC); +LL | | sample.not_very_safe(); +... | +LL | | asm!("nop"); +LL | | } + | |_____^ + | +note: union field access occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:58:14 + | +LL | drop(u.u); + | ^^^ +note: access of a mutable static occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:59:14 + | +LL | drop(STATIC); + | ^^^^^^ +note: unsafe method call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:60:9 + | +LL | sample.not_very_safe(); + | ^^^^^^^^^^^^^^^^^^^^^^ +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:61:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ +note: raw pointer dereference occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:62:9 + | +LL | *raw_ptr(); + | ^^^^^^^^^^ +note: inline assembly used here + --> $DIR/multiple_unsafe_ops_per_block.rs:63:9 + | +LL | asm!("nop"); + | ^^^^^^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:101:5 + | +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:101:14 + | +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: raw pointer dereference occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:101:39 + | +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + From 31a053059e773f08bddca53bbc3c7ca204daaf40 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Fri, 2 Dec 2022 16:27:25 +0100 Subject: [PATCH 474/524] Use UnordSet instead of FxHashSet in define_id_collections!(). --- clippy_lints/src/len_zero.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 9eba46756299..3c70c9cf19a5 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -219,7 +219,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items let is_empty = sym!(is_empty); let is_empty_method_found = current_and_super_traits - .iter() + .items() .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty)) .any(|i| { i.kind == ty::AssocKind::Fn From 465ed5bd46116e55d801a42a34659d558d00b0d7 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 17 Jan 2023 12:05:01 +0100 Subject: [PATCH 475/524] Use UnordMap instead of FxHashMap in define_id_collections!(). --- clippy_lints/src/inherent_impl.rs | 26 +++++++++---------- .../src/loops/while_immutable_condition.rs | 2 +- clippy_lints/src/missing_trait_methods.rs | 26 ++++++++++--------- clippy_lints/src/pass_by_ref_or_value.rs | 4 +-- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index c5abcc462545..81b37ce5dfc2 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -52,21 +52,19 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // List of spans to lint. (lint_span, first_span) let mut lint_spans = Vec::new(); - for (_, impl_ids) in cx + let inherent_impls = cx .tcx - .crate_inherent_impls(()) - .inherent_impls - .iter() - .filter(|(&id, impls)| { - impls.len() > 1 - // Check for `#[allow]` on the type definition - && !is_lint_allowed( - cx, - MULTIPLE_INHERENT_IMPL, - cx.tcx.hir().local_def_id_to_hir_id(id), - ) - }) - { + .with_stable_hashing_context(|hcx| cx.tcx.crate_inherent_impls(()).inherent_impls.to_sorted(&hcx)); + + for (_, impl_ids) in inherent_impls.into_iter().filter(|(&id, impls)| { + impls.len() > 1 + // Check for `#[allow]` on the type definition + && !is_lint_allowed( + cx, + MULTIPLE_INHERENT_IMPL, + cx.tcx.hir().local_def_id_to_hir_id(id), + ) + }) { for impl_id in impl_ids.iter().map(|id| id.expect_local()) { match type_map.entry(cx.tcx.type_of(impl_id)) { Entry::Vacant(e) => { diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index a63422d2a36a..d1a1f773f87b 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' } else { return; }; - let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v); + let mutable_static_in_cond = var_visitor.def_ids.items().any(|(_, v)| *v); let mut has_break_or_return_visitor = HasBreakOrReturnVisitor { has_break_or_return: false, diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs index 68af8a672f6a..1c61c6e551c3 100644 --- a/clippy_lints/src/missing_trait_methods.rs +++ b/clippy_lints/src/missing_trait_methods.rs @@ -80,19 +80,21 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { } } - for assoc in provided.values() { - let source_map = cx.tcx.sess.source_map(); - let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); + cx.tcx.with_stable_hashing_context(|hcx| { + for assoc in provided.values_sorted(&hcx) { + let source_map = cx.tcx.sess.source_map(); + let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); - span_lint_and_help( - cx, - MISSING_TRAIT_METHODS, - source_map.guess_head_span(item.span), - &format!("missing trait method provided by default: `{}`", assoc.name), - Some(definition_span), - "implement the method", - ); - } + span_lint_and_help( + cx, + MISSING_TRAIT_METHODS, + source_map.guess_head_span(item.span), + &format!("missing trait method provided by default: `{}`", assoc.name), + Some(definition_span), + "implement the method", + ); + } + }) } } } diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 870a1c7d88d5..2d21aaa4f7fd 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -190,10 +190,10 @@ impl<'tcx> PassByRefOrValue { // Don't lint if an unsafe pointer is created. // TODO: Limit the check only to unsafe pointers to the argument (or part of the argument) // which escape the current function. - if typeck.node_types().iter().any(|(_, &ty)| ty.is_unsafe_ptr()) + if typeck.node_types().items().any(|(_, &ty)| ty.is_unsafe_ptr()) || typeck .adjustments() - .iter() + .items() .flat_map(|(_, a)| a) .any(|a| matches!(a.kind, Adjust::Pointer(PointerCast::UnsafeFnPointer))) { From c1b358945a4e1d4877b21c02fa52afc5ff2b39a7 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Wed, 18 Jan 2023 10:47:31 +0100 Subject: [PATCH 476/524] Allow for more efficient sorting when exporting Unord collections. --- clippy_lints/src/inherent_impl.rs | 2 +- clippy_lints/src/missing_trait_methods.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 81b37ce5dfc2..e9b2e31a769a 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { let inherent_impls = cx .tcx - .with_stable_hashing_context(|hcx| cx.tcx.crate_inherent_impls(()).inherent_impls.to_sorted(&hcx)); + .with_stable_hashing_context(|hcx| cx.tcx.crate_inherent_impls(()).inherent_impls.to_sorted(&hcx, true)); for (_, impl_ids) in inherent_impls.into_iter().filter(|(&id, impls)| { impls.len() > 1 diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs index 1c61c6e551c3..3371b4cce32c 100644 --- a/clippy_lints/src/missing_trait_methods.rs +++ b/clippy_lints/src/missing_trait_methods.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { } cx.tcx.with_stable_hashing_context(|hcx| { - for assoc in provided.values_sorted(&hcx) { + for assoc in provided.values_sorted(&hcx, true) { let source_map = cx.tcx.sess.source_map(); let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); From 11611b0440d563e26758f7f4481e4fb66f4fe73f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 13 Jan 2023 00:04:28 +0000 Subject: [PATCH 477/524] Move `unchecked_duration_subtraction` to pedantic --- clippy_lints/src/instant_subtraction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index dd1b23e7d9d2..9f6e89405713 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -61,7 +61,7 @@ declare_clippy_lint! { /// [`Instant::now()`]: std::time::Instant::now; #[clippy::version = "1.65.0"] pub UNCHECKED_DURATION_SUBTRACTION, - suspicious, + pedantic, "finds unchecked subtraction of a 'Duration' from an 'Instant'" } From d9baced2b595d52d6927bb4696fa5067c1a427d1 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 19 Jan 2023 11:26:56 +0100 Subject: [PATCH 478/524] Improve the changelog update documentation - Make the clippy::version attribute instructions more prominent. - Mention the beta-accepted label. --- .../infrastructure/changelog_update.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/book/src/development/infrastructure/changelog_update.md b/book/src/development/infrastructure/changelog_update.md index 80a47affe30d..d1ac7237b5e3 100644 --- a/book/src/development/infrastructure/changelog_update.md +++ b/book/src/development/infrastructure/changelog_update.md @@ -95,11 +95,23 @@ As section headers, we use: Please also be sure to update the Beta/Unreleased sections at the top with the relevant commit ranges. -If you have the time, it would be appreciated if you double-check, that the -`#[clippy::version]` attributes for the added lints contains the correct version. +#### 3.1 Include `beta-accepted` PRs + +Look for the [`beta-accepted`] label and make sure to also include the PRs with +that label in the changelog. If you can, remove the `beta-accepted` labels +**after** the changelog PR was merged. + +> _Note:_ Some of those PRs might even got backported to the previous `beta`. +> Those have to be included in the changelog of the _previous_ release. + +### 4. Update `clippy::version` attributes + +Next, make sure to check that the `#[clippy::version]` attributes for the added +lints contain the correct version. [changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md [forge]: https://forge.rust-lang.org/ [rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy [rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy [rust_stable_tools]: https://github.com/rust-lang/rust/releases +[`beta-accepted`]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-accepted+ From e3a09eca6d810af2105b4ed3af17048b318d25c4 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 19 Jan 2023 21:57:56 +0100 Subject: [PATCH 479/524] Update version attribute for 1.67 lints --- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/from_raw_with_void_ptr.rs | 2 +- clippy_lints/src/instant_subtraction.rs | 2 +- clippy_lints/src/let_underscore.rs | 2 +- clippy_lints/src/manual_is_ascii_check.rs | 2 +- clippy_lints/src/methods/mod.rs | 4 ++-- clippy_lints/src/suspicious_xor_used_as_pow.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index cdc23a4d2273..f7a3d6d53f71 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -251,7 +251,7 @@ declare_clippy_lint! { /// unimplemented!(); /// } /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.67.0"] pub UNNECESSARY_SAFETY_DOC, restriction, "`pub fn` or `pub trait` with `# Safety` docs" diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs index 00f5ba56496e..096508dc4f11 100644 --- a/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -31,7 +31,7 @@ declare_clippy_lint! { /// let _ = unsafe { Box::from_raw(ptr as *mut usize) }; /// ``` /// - #[clippy::version = "1.66.0"] + #[clippy::version = "1.67.0"] pub FROM_RAW_WITH_VOID_PTR, suspicious, "creating a `Box` from a void raw pointer" diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 9f6e89405713..668110c7cc08 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -59,7 +59,7 @@ declare_clippy_lint! { /// /// [`Duration`]: std::time::Duration /// [`Instant::now()`]: std::time::Instant::now; - #[clippy::version = "1.65.0"] + #[clippy::version = "1.67.0"] pub UNCHECKED_DURATION_SUBTRACTION, pedantic, "finds unchecked subtraction of a 'Duration' from an 'Instant'" diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 61f87b91400d..f8e359509808 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -84,7 +84,7 @@ declare_clippy_lint! { /// let _ = foo().await; /// # } /// ``` - #[clippy::version = "1.66"] + #[clippy::version = "1.67.0"] pub LET_UNDERSCORE_FUTURE, suspicious, "non-binding `let` on a future" diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index d9ef7dffa020..2fd32c009eaa 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { /// 'A'.is_ascii_uppercase(); /// } /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.67.0"] pub MANUAL_IS_ASCII_CHECK, style, "use dedicated method to check ascii range" diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 77be61b47934..42377a3d138c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3102,7 +3102,7 @@ declare_clippy_lint! { /// Ok(()) /// } /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.67.0"] pub SEEK_FROM_CURRENT, complexity, "use dedicated method for seek from current position" @@ -3133,7 +3133,7 @@ declare_clippy_lint! { /// t.rewind(); /// } /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.67.0"] pub SEEK_TO_START_INSTEAD_OF_REWIND, complexity, "jumping to the start of stream using `seek` method" diff --git a/clippy_lints/src/suspicious_xor_used_as_pow.rs b/clippy_lints/src/suspicious_xor_used_as_pow.rs index 301aa5798bf5..c181919b1647 100644 --- a/clippy_lints/src/suspicious_xor_used_as_pow.rs +++ b/clippy_lints/src/suspicious_xor_used_as_pow.rs @@ -18,7 +18,7 @@ declare_clippy_lint! { /// ```rust /// let x = 3_i32.pow(4); /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.67.0"] pub SUSPICIOUS_XOR_USED_AS_POW, restriction, "XOR (`^`) operator possibly used as exponentiation operator" From f64efd4d318f92cb6b7d0836826fcfe66d920727 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 19 Jan 2023 21:52:57 +0100 Subject: [PATCH 480/524] Changelog for Rust 1.67 :lady_beetle: --- CHANGELOG.md | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 196 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84f4654f34e4..073691ad61ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,204 @@ document. ## Unreleased / Beta / In Rust Nightly -[4f142aa1...master](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...master) +[d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master) + +## Rust 1.67 + +Current stable, released 2023-01-26 + +[4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d) + +### New Lints + +* [`seek_from_current`] + [#9681](https://github.com/rust-lang/rust-clippy/pull/9681) +* [`from_raw_with_void_ptr`] + [#9690](https://github.com/rust-lang/rust-clippy/pull/9690) +* [`misnamed_getters`] + [#9770](https://github.com/rust-lang/rust-clippy/pull/9770) +* [`seek_to_start_instead_of_rewind`] + [#9667](https://github.com/rust-lang/rust-clippy/pull/9667) +* [`suspicious_xor_used_as_pow`] + [#9506](https://github.com/rust-lang/rust-clippy/pull/9506) +* [`unnecessary_safety_doc`] + [#9822](https://github.com/rust-lang/rust-clippy/pull/9822) +* [`unchecked_duration_subtraction`] + [#9570](https://github.com/rust-lang/rust-clippy/pull/9570) +* [`manual_is_ascii_check`] + [#9765](https://github.com/rust-lang/rust-clippy/pull/9765) +* [`unnecessary_safety_comment`] + [#9851](https://github.com/rust-lang/rust-clippy/pull/9851) +* [`let_underscore_future`] + [#9760](https://github.com/rust-lang/rust-clippy/pull/9760) +* [`manual_let_else`] + [#8437](https://github.com/rust-lang/rust-clippy/pull/8437) + +### Moves and Deprecations + +* Moved [`uninlined_format_args`] to `style` (Now warn-by-default) + [#9865](https://github.com/rust-lang/rust-clippy/pull/9865) +* Moved [`needless_collect`] to `nursery` (Now allow-by-default) + [#9705](https://github.com/rust-lang/rust-clippy/pull/9705) +* Moved [`or_fun_call`] to `nursery` (Now allow-by-default) + [#9829](https://github.com/rust-lang/rust-clippy/pull/9829) +* Uplifted [`let_underscore_lock`] into rustc + [#9697](https://github.com/rust-lang/rust-clippy/pull/9697) +* Uplifted [`let_underscore_drop`] into rustc + [#9697](https://github.com/rust-lang/rust-clippy/pull/9697) +* Moved [`bool_to_int_with_if`] to `pedantic` (Now allow-by-default) + [#9830](https://github.com/rust-lang/rust-clippy/pull/9830) +* [`manual_swap`]: No longer lints in const context + [#9871](https://github.com/rust-lang/rust-clippy/pull/9871) +* Move `index_refutable_slice` to `pedantic` (Now warn-by-default) + [#9975](https://github.com/rust-lang/rust-clippy/pull/9975) +* Moved [`manual_clamp`] to `nursery` (Now allow-by-default) + [#10101](https://github.com/rust-lang/rust-clippy/pull/10101) + +### Enhancements + +* The scope of `#![clippy::msrv]` is now tracked correctly + [#9924](https://github.com/rust-lang/rust-clippy/pull/9924) +* `#[clippy::msrv]` can now be used as an outer attribute + [#9860](https://github.com/rust-lang/rust-clippy/pull/9860) +* Clippy will now avoid Cargo's cache, if `Cargo.toml` or `clippy.toml` have changed + [#9707](https://github.com/rust-lang/rust-clippy/pull/9707) +* [`uninlined_format_args`]: Added a new config `allow-mixed-uninlined-format-args` to allow the + lint, if only some arguments can be inlined + [#9865](https://github.com/rust-lang/rust-clippy/pull/9865) +* [`needless_lifetimes`]: Now provides suggests for individual lifetimes + [#9743](https://github.com/rust-lang/rust-clippy/pull/9743) +* [`needless_collect`]: Now detects needless `is_empty` and `contains` calls + [#8744](https://github.com/rust-lang/rust-clippy/pull/8744) +* [`blanket_clippy_restriction_lints`]: Now lints, if `clippy::restriction` is enabled via the + command line arguments + [#9755](https://github.com/rust-lang/rust-clippy/pull/9755) +* [`mutable_key_type`]: Now has the `ignore-interior-mutability` configuration, to add types which + should be ignored by the lint + [#9692](https://github.com/rust-lang/rust-clippy/pull/9692) +* [`uninlined_format_args`]: Now works for multiline `format!` expressions + [#9945](https://github.com/rust-lang/rust-clippy/pull/9945) +* [`cognitive_complexity`]: Now works for async functions + [#9828](https://github.com/rust-lang/rust-clippy/pull/9828) + [#9836](https://github.com/rust-lang/rust-clippy/pull/9836) +* [`vec_box`]: Now avoids an off-by-one error when using the `vec-box-size-threshold` configuration + [#9848](https://github.com/rust-lang/rust-clippy/pull/9848) +* [`never_loop`]: Now correctly handles breaks in nested labeled blocks + [#9858](https://github.com/rust-lang/rust-clippy/pull/9858) + [#9837](https://github.com/rust-lang/rust-clippy/pull/9837) +* [`disallowed_methods`], [`disallowed_types`], [`disallowed_macros`]: Now correctly resolve + paths, if a crate is used multiple times with different versions + [#9800](https://github.com/rust-lang/rust-clippy/pull/9800) +* [`disallowed_methods`]: Can now be used for local methods + [#9800](https://github.com/rust-lang/rust-clippy/pull/9800) +* [`print_stdout`], [`print_stderr`]: Can now be enabled in test with the `allow-print-in-tests` + config value + [#9797](https://github.com/rust-lang/rust-clippy/pull/9797) +* [`from_raw_with_void_ptr`]: Now works for `Rc`, `Arc`, `alloc::rc::Weak` and + `alloc::sync::Weak` types. + [#9700](https://github.com/rust-lang/rust-clippy/pull/9700) +* [`needless_borrowed_reference`]: Now works for struct and tuple patterns with wildcards + [#9855](https://github.com/rust-lang/rust-clippy/pull/9855) +* [`or_fun_call`]: Now supports `map_or` methods + [#9689](https://github.com/rust-lang/rust-clippy/pull/9689) +* [`unwrap_used`], [`expect_used`]: No longer lints in test code + [#9686](https://github.com/rust-lang/rust-clippy/pull/9686) +* [`fn_params_excessive_bools`]: Is now emitted with the lint level at the linted function + [#9698](https://github.com/rust-lang/rust-clippy/pull/9698) + +### False Positive Fixes + +* [`new_ret_no_self`]: No longer lints when `impl Trait` is returned + [#9733](https://github.com/rust-lang/rust-clippy/pull/9733) +* [`unnecessary_lazy_evaluations`]: No longer lints, if the type has a significant drop + [#9750](https://github.com/rust-lang/rust-clippy/pull/9750) +* [`option_if_let_else`]: No longer lints, if any arm has guard + [#9747](https://github.com/rust-lang/rust-clippy/pull/9747) +* [`explicit_auto_deref`]: No longer lints, if the target type is a projection with generic + arguments + [#9813](https://github.com/rust-lang/rust-clippy/pull/9813) +* [`unnecessary_to_owned`]: No longer lints, if the suggestion effects types + [#9796](https://github.com/rust-lang/rust-clippy/pull/9796) +* [`needless_borrow`]: No longer lints, if the suggestion is affected by `Deref` + [#9674](https://github.com/rust-lang/rust-clippy/pull/9674) +* [`unused_unit`]: No longer lints, if lifetimes are bound to the return type + [#9849](https://github.com/rust-lang/rust-clippy/pull/9849) +* [`mut_mut`]: No longer lints cases with unsized mutable references + [#9835](https://github.com/rust-lang/rust-clippy/pull/9835) +* [`bool_to_int_with_if`]: No longer lints in const context + [#9738](https://github.com/rust-lang/rust-clippy/pull/9738) +* [`use_self`]: No longer lints in macros + [#9704](https://github.com/rust-lang/rust-clippy/pull/9704) +* [`unnecessary_operation`]: No longer lints, if multiple macros are involved + [#9981](https://github.com/rust-lang/rust-clippy/pull/9981) +* [`allow_attributes_without_reason`]: No longer lints inside external macros + [#9630](https://github.com/rust-lang/rust-clippy/pull/9630) +* [`question_mark`]: No longer lints for `if let Err()` with an `else` branch + [#9722](https://github.com/rust-lang/rust-clippy/pull/9722) +* [`unnecessary_cast`]: No longer lints if the identifier and cast originate from different macros + [#9980](https://github.com/rust-lang/rust-clippy/pull/9980) +* [`arithmetic_side_effects`]: Now detects operations with associated constants + [#9592](https://github.com/rust-lang/rust-clippy/pull/9592) +* [`explicit_auto_deref`]: No longer lints, if the initial value is not a reference or reference + receiver + [#9997](https://github.com/rust-lang/rust-clippy/pull/9997) +* [`module_name_repetitions`], [`single_component_path_imports`]: Now handle `#[allow]` + attributes correctly + [#9879](https://github.com/rust-lang/rust-clippy/pull/9879) +* [`bool_to_int_with_if`]: No longer lints `if let` statements + [#9714](https://github.com/rust-lang/rust-clippy/pull/9714) +* [`needless_borrow`]: No longer lints, `if`-`else`-statements that require the borrow + [#9791](https://github.com/rust-lang/rust-clippy/pull/9791) +* [`needless_borrow`]: No longer lints borrows, if moves were illegal + [#9711](https://github.com/rust-lang/rust-clippy/pull/9711) + +### Suggestion Fixes/Improvements + +* [`missing_safety_doc`], [`missing_errors_doc`], [`missing_panics_doc`]: No longer show the + entire item in the lint emission. + [#9772](https://github.com/rust-lang/rust-clippy/pull/9772) +* [`needless_lifetimes`]: Only suggests `'_` when it's applicable + [#9743](https://github.com/rust-lang/rust-clippy/pull/9743) +* [`use_self`]: Now suggests full paths correctly + [#9726](https://github.com/rust-lang/rust-clippy/pull/9726) +* [`redundant_closure_call`]: Now correctly deals with macros during suggestion creation + [#9987](https://github.com/rust-lang/rust-clippy/pull/9987) +* [`unnecessary_cast`]: Suggestions now correctly deal with references + [#9996](https://github.com/rust-lang/rust-clippy/pull/9996) +* [`unnecessary_join`]: Suggestions now correctly use [turbofish] operators + [#9779](https://github.com/rust-lang/rust-clippy/pull/9779) +* [`equatable_if_let`]: Can now suggest `matches!` replacements + [#9368](https://github.com/rust-lang/rust-clippy/pull/9368) +* [`string_extend_chars`]: Suggestions now correctly work for `str` slices + [#9741](https://github.com/rust-lang/rust-clippy/pull/9741) +* [`redundant_closure_for_method_calls`]: Suggestions now include angle brackets and generic + arguments if needed + [#9745](https://github.com/rust-lang/rust-clippy/pull/9745) +* [`manual_let_else`]: Suggestions no longer expand macro calls + [#9943](https://github.com/rust-lang/rust-clippy/pull/9943) +* [`infallible_destructuring_match`]: Suggestions now preserve references + [#9850](https://github.com/rust-lang/rust-clippy/pull/9850) +* [`result_large_err`]: The error now shows the largest enum variant + [#9662](https://github.com/rust-lang/rust-clippy/pull/9662) +* [`needless_return`]: Suggestions are now formatted better + [#9967](https://github.com/rust-lang/rust-clippy/pull/9967) +* [`unused_rounding`]: The suggestion now preserves the original float literal notation + [#9870](https://github.com/rust-lang/rust-clippy/pull/9870) + +[turbofish]: https://turbo.fish/::%3CClippy%3E + +### ICE Fixes + +* [`result_large_err`]: Fixed ICE for empty enums + [#10007](https://github.com/rust-lang/rust-clippy/pull/10007) +* [`redundant_allocation`]: Fixed ICE for types with bounded variables + [#9773](https://github.com/rust-lang/rust-clippy/pull/9773) +* [`unused_rounding`]: Fixed ICE, if `_` was used as a separator + [#10001](https://github.com/rust-lang/rust-clippy/pull/10001) ## Rust 1.66 -Current stable, released 2022-12-15 +Released 2022-12-15 [b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1) @@ -166,6 +359,7 @@ Current stable, released 2022-12-15 * [`unnecessary_to_owned`]: Avoid ICEs in favor of false negatives if information is missing [#9505](https://github.com/rust-lang/rust-clippy/pull/9505) + [#10027](https://github.com/rust-lang/rust-clippy/pull/10027) * [`manual_range_contains`]: No longer ICEs on values behind references [#9627](https://github.com/rust-lang/rust-clippy/pull/9627) * [`needless_pass_by_value`]: No longer ICEs on unsized `dyn Fn` arguments From a7ae84bc84fbe35d3f07c4fb5c325e130b0b9d19 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 20 Jan 2023 09:21:41 +0100 Subject: [PATCH 481/524] Address PR feedback and change text for early merge --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 073691ad61ce..d3cb880df57b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,13 @@ All notable changes to this project will be documented in this file. See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this document. -## Unreleased / Beta / In Rust Nightly +## Unreleased / In Rust Nightly [d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master) ## Rust 1.67 -Current stable, released 2023-01-26 +Current beta, released 2023-01-26 [4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d) @@ -53,8 +53,6 @@ Current stable, released 2023-01-26 [#9697](https://github.com/rust-lang/rust-clippy/pull/9697) * Moved [`bool_to_int_with_if`] to `pedantic` (Now allow-by-default) [#9830](https://github.com/rust-lang/rust-clippy/pull/9830) -* [`manual_swap`]: No longer lints in const context - [#9871](https://github.com/rust-lang/rust-clippy/pull/9871) * Move `index_refutable_slice` to `pedantic` (Now warn-by-default) [#9975](https://github.com/rust-lang/rust-clippy/pull/9975) * Moved [`manual_clamp`] to `nursery` (Now allow-by-default) @@ -156,6 +154,8 @@ Current stable, released 2023-01-26 [#9791](https://github.com/rust-lang/rust-clippy/pull/9791) * [`needless_borrow`]: No longer lints borrows, if moves were illegal [#9711](https://github.com/rust-lang/rust-clippy/pull/9711) +* [`manual_swap`]: No longer lints in const context + [#9871](https://github.com/rust-lang/rust-clippy/pull/9871) ### Suggestion Fixes/Improvements @@ -203,7 +203,7 @@ Current stable, released 2023-01-26 ## Rust 1.66 -Released 2022-12-15 +Current stable, released 2022-12-15 [b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1) From 081c6178fe9e8b1a0dd311c6648263dedff35b83 Mon Sep 17 00:00:00 2001 From: chansuke Date: Fri, 20 Jan 2023 20:25:31 +0900 Subject: [PATCH 482/524] Fix spelling inconsistence of `mdBook` --- book/src/development/infrastructure/book.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/book/src/development/infrastructure/book.md b/book/src/development/infrastructure/book.md index a48742191850..dbd624ecd738 100644 --- a/book/src/development/infrastructure/book.md +++ b/book/src/development/infrastructure/book.md @@ -3,15 +3,15 @@ This document explains how to make additions and changes to the Clippy book, the guide to Clippy that you're reading right now. The Clippy book is formatted with [Markdown](https://www.markdownguide.org) and generated by -[mdbook](https://github.com/rust-lang/mdBook). +[mdBook](https://github.com/rust-lang/mdBook). -- [Get mdbook](#get-mdbook) +- [Get mdBook](#get-mdbook) - [Make changes](#make-changes) -## Get mdbook +## Get mdBook While not strictly necessary since the book source is simply Markdown text -files, having mdbook locally will allow you to build, test and serve the book +files, having mdBook locally will allow you to build, test and serve the book locally to view changes before you commit them to the repository. You likely already have `cargo` installed, so the easiest option is to simply: @@ -19,7 +19,7 @@ already have `cargo` installed, so the easiest option is to simply: cargo install mdbook ``` -See the mdbook [installation](https://github.com/rust-lang/mdBook#installation) +See the mdBook [installation](https://github.com/rust-lang/mdBook#installation) instructions for other options. ## Make changes @@ -27,7 +27,7 @@ instructions for other options. The book's [src](https://github.com/rust-lang/rust-clippy/tree/master/book/src) directory contains all of the markdown files used to generate the book. If you -want to see your changes in real time, you can use the mdbook `serve` command to +want to see your changes in real time, you can use the mdBook `serve` command to run a web server locally that will automatically update changes as they are made. From the top level of your `rust-clippy` directory: @@ -38,5 +38,5 @@ mdbook serve book --open Then navigate to `http://localhost:3000` to see the generated book. While the server is running, changes you make will automatically be updated. -For more information, see the mdbook +For more information, see the mdBook [guide](https://rust-lang.github.io/mdBook/). From 5f49808bdefc42deb06f5096854ec2f910848cb0 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 21 Jan 2023 17:20:46 +0000 Subject: [PATCH 483/524] Add machine applicable suggestion for `bool_assert_comparison` --- clippy_lints/src/bool_assert_comparison.rs | 53 +++-- tests/ui/bool_assert_comparison.fixed | 161 +++++++++++++ tests/ui/bool_assert_comparison.rs | 43 +++- tests/ui/bool_assert_comparison.stderr | 257 +++++++++++++++++---- 4 files changed, 453 insertions(+), 61 deletions(-) create mode 100644 tests/ui/bool_assert_comparison.fixed diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 82d368bb8bc2..556fa579000c 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -1,10 +1,11 @@ +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait}; +use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Lit}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; @@ -43,9 +44,7 @@ fn is_bool_lit(e: &Expr<'_>) -> bool { ) && !e.span.from_expansion() } -fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(e); - +fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { cx.tcx .lang_items() .not_trait() @@ -77,31 +76,57 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { return; } let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; - if !(is_bool_lit(a) ^ is_bool_lit(b)) { + + let a_span = a.span.source_callsite(); + let b_span = b.span.source_callsite(); + + let (lit_span, non_lit_expr) = match (is_bool_lit(a), is_bool_lit(b)) { + // assert_eq!(true, b) + // ^^^^^^ + (true, false) => (a_span.until(b_span), b), + // assert_eq!(a, true) + // ^^^^^^ + (false, true) => (b_span.with_lo(a_span.hi()), a), // If there are two boolean arguments, we definitely don't understand // what's going on, so better leave things as is... // // Or there is simply no boolean and then we can leave things as is! - return; - } + _ => return, + }; - if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { + let non_lit_ty = cx.typeck_results().expr_ty(non_lit_expr); + + if !is_impl_not_trait_with_bool_out(cx, non_lit_ty) { // At this point the expression which is not a boolean // literal does not implement Not trait with a bool output, // so we cannot suggest to rewrite our code return; } + if !is_copy(cx, non_lit_ty) { + // Only lint with types that are `Copy` because `assert!(x)` takes + // ownership of `x` whereas `assert_eq(x, true)` does not + return; + } + let macro_name = macro_name.as_str(); let non_eq_mac = ¯o_name[..macro_name.len() - 3]; - span_lint_and_sugg( + span_lint_and_then( cx, BOOL_ASSERT_COMPARISON, macro_call.span, &format!("used `{macro_name}!` with a literal bool"), - "replace it with", - format!("{non_eq_mac}!(..)"), - Applicability::MaybeIncorrect, + |diag| { + // assert_eq!(...) + // ^^^^^^^^^ + let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); + + diag.multipart_suggestion( + format!("replace it with `{non_eq_mac}!(..)`"), + vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())], + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/tests/ui/bool_assert_comparison.fixed b/tests/ui/bool_assert_comparison.fixed new file mode 100644 index 000000000000..95f35a61bb28 --- /dev/null +++ b/tests/ui/bool_assert_comparison.fixed @@ -0,0 +1,161 @@ +// run-rustfix + +#![allow(unused, clippy::assertions_on_constants)] +#![warn(clippy::bool_assert_comparison)] + +use std::ops::Not; + +macro_rules! a { + () => { + true + }; +} +macro_rules! b { + () => { + true + }; +} + +// Implements the Not trait but with an output type +// that's not bool. Should not suggest a rewrite +#[derive(Debug, Clone, Copy)] +enum ImplNotTraitWithoutBool { + VariantX(bool), + VariantY(u32), +} + +impl PartialEq for ImplNotTraitWithoutBool { + fn eq(&self, other: &bool) -> bool { + match *self { + ImplNotTraitWithoutBool::VariantX(b) => b == *other, + _ => false, + } + } +} + +impl Not for ImplNotTraitWithoutBool { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + ImplNotTraitWithoutBool::VariantX(b) => ImplNotTraitWithoutBool::VariantX(!b), + ImplNotTraitWithoutBool::VariantY(0) => ImplNotTraitWithoutBool::VariantY(1), + ImplNotTraitWithoutBool::VariantY(_) => ImplNotTraitWithoutBool::VariantY(0), + } + } +} + +// This type implements the Not trait with an Output of +// type bool. Using assert!(..) must be suggested +#[derive(Debug, Clone, Copy)] +struct ImplNotTraitWithBool; + +impl PartialEq for ImplNotTraitWithBool { + fn eq(&self, other: &bool) -> bool { + false + } +} + +impl Not for ImplNotTraitWithBool { + type Output = bool; + + fn not(self) -> Self::Output { + true + } +} + +#[derive(Debug)] +struct NonCopy; + +impl PartialEq for NonCopy { + fn eq(&self, other: &bool) -> bool { + false + } +} + +impl Not for NonCopy { + type Output = bool; + + fn not(self) -> Self::Output { + true + } +} + +fn main() { + let a = ImplNotTraitWithoutBool::VariantX(true); + let b = ImplNotTraitWithBool; + + assert_eq!("a".len(), 1); + assert!("a".is_empty()); + assert!("".is_empty()); + assert!("".is_empty()); + assert_eq!(a!(), b!()); + assert_eq!(a!(), "".is_empty()); + assert_eq!("".is_empty(), b!()); + assert_eq!(a, true); + assert!(b); + + assert_ne!("a".len(), 1); + assert!("a".is_empty()); + assert!("".is_empty()); + assert!("".is_empty()); + assert_ne!(a!(), b!()); + assert_ne!(a!(), "".is_empty()); + assert_ne!("".is_empty(), b!()); + assert_ne!(a, true); + assert!(b); + + debug_assert_eq!("a".len(), 1); + debug_assert!("a".is_empty()); + debug_assert!("".is_empty()); + debug_assert!("".is_empty()); + debug_assert_eq!(a!(), b!()); + debug_assert_eq!(a!(), "".is_empty()); + debug_assert_eq!("".is_empty(), b!()); + debug_assert_eq!(a, true); + debug_assert!(b); + + debug_assert_ne!("a".len(), 1); + debug_assert!("a".is_empty()); + debug_assert!("".is_empty()); + debug_assert!("".is_empty()); + debug_assert_ne!(a!(), b!()); + debug_assert_ne!(a!(), "".is_empty()); + debug_assert_ne!("".is_empty(), b!()); + debug_assert_ne!(a, true); + debug_assert!(b); + + // assert with error messages + assert_eq!("a".len(), 1, "tadam {}", 1); + assert_eq!("a".len(), 1, "tadam {}", true); + assert!("a".is_empty(), "tadam {}", 1); + assert!("a".is_empty(), "tadam {}", true); + assert!("a".is_empty(), "tadam {}", true); + assert_eq!(a, true, "tadam {}", false); + + debug_assert_eq!("a".len(), 1, "tadam {}", 1); + debug_assert_eq!("a".len(), 1, "tadam {}", true); + debug_assert!("a".is_empty(), "tadam {}", 1); + debug_assert!("a".is_empty(), "tadam {}", true); + debug_assert!("a".is_empty(), "tadam {}", true); + debug_assert_eq!(a, true, "tadam {}", false); + + assert!(a!()); + assert!(b!()); + + use debug_assert_eq as renamed; + renamed!(a, true); + debug_assert!(b); + + let non_copy = NonCopy; + assert_eq!(non_copy, true); + // changing the above to `assert!(non_copy)` would cause a `borrow of moved value` + println!("{non_copy:?}"); + + macro_rules! in_macro { + ($v:expr) => {{ + assert_eq!($v, true); + }}; + } + in_macro!(a); +} diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs index ec4d6f3ff840..88e7560b4f98 100644 --- a/tests/ui/bool_assert_comparison.rs +++ b/tests/ui/bool_assert_comparison.rs @@ -1,3 +1,6 @@ +// run-rustfix + +#![allow(unused, clippy::assertions_on_constants)] #![warn(clippy::bool_assert_comparison)] use std::ops::Not; @@ -15,7 +18,7 @@ macro_rules! b { // Implements the Not trait but with an output type // that's not bool. Should not suggest a rewrite -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] enum ImplNotTraitWithoutBool { VariantX(bool), VariantY(u32), @@ -44,7 +47,7 @@ impl Not for ImplNotTraitWithoutBool { // This type implements the Not trait with an Output of // type bool. Using assert!(..) must be suggested -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] struct ImplNotTraitWithBool; impl PartialEq for ImplNotTraitWithBool { @@ -61,6 +64,23 @@ impl Not for ImplNotTraitWithBool { } } +#[derive(Debug)] +struct NonCopy; + +impl PartialEq for NonCopy { + fn eq(&self, other: &bool) -> bool { + false + } +} + +impl Not for NonCopy { + type Output = bool; + + fn not(self) -> Self::Output { + true + } +} + fn main() { let a = ImplNotTraitWithoutBool::VariantX(true); let b = ImplNotTraitWithBool; @@ -119,4 +139,23 @@ fn main() { debug_assert_eq!("a".is_empty(), false, "tadam {}", true); debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); debug_assert_eq!(a, true, "tadam {}", false); + + assert_eq!(a!(), true); + assert_eq!(true, b!()); + + use debug_assert_eq as renamed; + renamed!(a, true); + renamed!(b, true); + + let non_copy = NonCopy; + assert_eq!(non_copy, true); + // changing the above to `assert!(non_copy)` would cause a `borrow of moved value` + println!("{non_copy:?}"); + + macro_rules! in_macro { + ($v:expr) => {{ + assert_eq!($v, true); + }}; + } + in_macro!(a); } diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr index 377d51be4cde..3d9f8573e617 100644 --- a/tests/ui/bool_assert_comparison.stderr +++ b/tests/ui/bool_assert_comparison.stderr @@ -1,136 +1,303 @@ error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:69:5 + --> $DIR/bool_assert_comparison.rs:89:5 | LL | assert_eq!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::bool-assert-comparison` implied by `-D warnings` +help: replace it with `assert!(..)` + | +LL - assert_eq!("a".is_empty(), false); +LL + assert!("a".is_empty()); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:70:5 + --> $DIR/bool_assert_comparison.rs:90:5 | LL | assert_eq!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("".is_empty(), true); +LL + assert!("".is_empty()); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:71:5 + --> $DIR/bool_assert_comparison.rs:91:5 | LL | assert_eq!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(true, "".is_empty()); +LL + assert!("".is_empty()); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:76:5 + --> $DIR/bool_assert_comparison.rs:96:5 | LL | assert_eq!(b, true); - | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(b, true); +LL + assert!(b); + | error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:79:5 + --> $DIR/bool_assert_comparison.rs:99:5 | LL | assert_ne!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!("a".is_empty(), false); +LL + assert!("a".is_empty()); + | error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:80:5 + --> $DIR/bool_assert_comparison.rs:100:5 | LL | assert_ne!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!("".is_empty(), true); +LL + assert!("".is_empty()); + | error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:81:5 + --> $DIR/bool_assert_comparison.rs:101:5 | LL | assert_ne!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!(true, "".is_empty()); +LL + assert!("".is_empty()); + | error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:86:5 + --> $DIR/bool_assert_comparison.rs:106:5 | LL | assert_ne!(b, true); - | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!(b, true); +LL + assert!(b); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:89:5 + --> $DIR/bool_assert_comparison.rs:109:5 | LL | debug_assert_eq!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("a".is_empty(), false); +LL + debug_assert!("a".is_empty()); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:90:5 + --> $DIR/bool_assert_comparison.rs:110:5 | LL | debug_assert_eq!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("".is_empty(), true); +LL + debug_assert!("".is_empty()); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:91:5 + --> $DIR/bool_assert_comparison.rs:111:5 | LL | debug_assert_eq!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!(true, "".is_empty()); +LL + debug_assert!("".is_empty()); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:96:5 + --> $DIR/bool_assert_comparison.rs:116:5 | LL | debug_assert_eq!(b, true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!(b, true); +LL + debug_assert!(b); + | error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:99:5 + --> $DIR/bool_assert_comparison.rs:119:5 | LL | debug_assert_ne!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!("a".is_empty(), false); +LL + debug_assert!("a".is_empty()); + | error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:100:5 + --> $DIR/bool_assert_comparison.rs:120:5 | LL | debug_assert_ne!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!("".is_empty(), true); +LL + debug_assert!("".is_empty()); + | error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:101:5 + --> $DIR/bool_assert_comparison.rs:121:5 | LL | debug_assert_ne!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!(true, "".is_empty()); +LL + debug_assert!("".is_empty()); + | error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:106:5 + --> $DIR/bool_assert_comparison.rs:126:5 | LL | debug_assert_ne!(b, true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!(b, true); +LL + debug_assert!(b); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:111:5 + --> $DIR/bool_assert_comparison.rs:131:5 | LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("a".is_empty(), false, "tadam {}", 1); +LL + assert!("a".is_empty(), "tadam {}", 1); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:112:5 + --> $DIR/bool_assert_comparison.rs:132:5 | LL | assert_eq!("a".is_empty(), false, "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("a".is_empty(), false, "tadam {}", true); +LL + assert!("a".is_empty(), "tadam {}", true); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:113:5 + --> $DIR/bool_assert_comparison.rs:133:5 | LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(false, "a".is_empty(), "tadam {}", true); +LL + assert!("a".is_empty(), "tadam {}", true); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:118:5 + --> $DIR/bool_assert_comparison.rs:138:5 | LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); +LL + debug_assert!("a".is_empty(), "tadam {}", 1); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:119:5 + --> $DIR/bool_assert_comparison.rs:139:5 | LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", true); +LL + debug_assert!("a".is_empty(), "tadam {}", true); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:120:5 + --> $DIR/bool_assert_comparison.rs:140:5 | LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); +LL + debug_assert!("a".is_empty(), "tadam {}", true); + | + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:143:5 + | +LL | assert_eq!(a!(), true); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(a!(), true); +LL + assert!(a!()); + | + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:144:5 + | +LL | assert_eq!(true, b!()); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(true, b!()); +LL + assert!(b!()); + | + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:148:5 + | +LL | renamed!(b, true); + | ^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - renamed!(b, true); +LL + debug_assert!(b); + | -error: aborting due to 22 previous errors +error: aborting due to 25 previous errors From 8874edd238761206cf732740b5b90558ad7f73e5 Mon Sep 17 00:00:00 2001 From: Samuel Moelius <35515885+smoelius@users.noreply.github.com> Date: Sun, 22 Jan 2023 06:41:59 -0500 Subject: [PATCH 484/524] Update main.rs --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 7a78b32620d0..82147eba33f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,7 @@ with: -D --deny OPT Set lint denied -F --forbid OPT Set lint forbidden -You can use tool lints to allow or deny lints from your code, eg.: +You can use tool lints to allow or deny lints from your code, e.g.: #[allow(clippy::needless_lifetimes)] "#; From 2d8ede23353266c38f0b7c91be934495e941f2cc Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 15 Jan 2023 12:58:46 +0100 Subject: [PATCH 485/524] fix: use LocalDefId instead of HirId in trait res use LocalDefId instead of HirId in trait resolution to simplify the obligation clause resolution Signed-off-by: Vincenzo Palazzo --- clippy_lints/src/future_not_send.rs | 3 ++- clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/transmute/utils.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 989f83cf80d5..2a79b18b8299 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -78,7 +78,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap(); let span = decl.output.span(); let infcx = cx.tcx.infer_ctxt().build(); - let cause = traits::ObligationCause::misc(span, hir_id); + let def_id = cx.tcx.hir().local_def_id(hir_id); + let cause = traits::ObligationCause::misc(span, def_id); let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait); if !send_errors.is_empty() { span_lint_and_then( diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 9263f0519724..b812e81cb107 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -371,7 +371,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< && let output_ty = return_ty(cx, item.hir_id()) && let local_def_id = cx.tcx.hir().local_def_id(item.hir_id()) && Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.hir_id()); + let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id); fn_ctxt.can_coerce(ty, output_ty) }) { if has_lifetime(output_ty) && has_lifetime(ty) { diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 49d863ec03f1..b59d52dfc4d3 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -46,7 +46,7 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx> let local_def_id = hir_id.owner.def_id; Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(inherited, cx.param_env, hir_id); + let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id); // If we already have errors, we can't be sure we can pointer cast. assert!( From f9f75e093259384ec2d9fb83cbc799a27d894fea Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Tue, 24 Jan 2023 18:18:35 -0500 Subject: [PATCH 486/524] Lint `unused_io_amount` with `is_ok` and `is_err` --- clippy_lints/src/unused_io_amount.rs | 2 +- tests/ui/unused_io_amount.rs | 8 +++++ tests/ui/unused_io_amount.stderr | 44 ++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 92053cec59fc..0e526c216bee 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } }, hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() { - "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { + "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" | "is_ok" | "is_err" => { check_map_error(cx, arg_0, expr); }, _ => (), diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index 4b059558173f..8d3e094b7596 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -63,6 +63,14 @@ fn combine_or(file: &str) -> Result<(), Error> { Ok(()) } +fn is_ok_err(s: &mut T) { + s.write(b"ok").is_ok(); + s.write(b"err").is_err(); + let mut buf = [0u8; 0]; + s.read(&mut buf).is_ok(); + s.read(&mut buf).is_err(); +} + async fn bad_async_write(w: &mut W) { w.write(b"hello world").await.unwrap(); } diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr index 7ba7e09c0f0d..0865c5213f68 100644 --- a/tests/ui/unused_io_amount.stderr +++ b/tests/ui/unused_io_amount.stderr @@ -82,13 +82,45 @@ LL | | .expect("error"); error: written amount is not handled --> $DIR/unused_io_amount.rs:67:5 | +LL | s.write(b"ok").is_ok(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:68:5 + | +LL | s.write(b"err").is_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:70:5 + | +LL | s.read(&mut buf).is_ok(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:71:5 + | +LL | s.read(&mut buf).is_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:75:5 + | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:72:5 + --> $DIR/unused_io_amount.rs:80:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | r.read(&mut buf[..]).await.unwrap(); = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:85:9 + --> $DIR/unused_io_amount.rs:93:9 | LL | w.write(b"hello world").await?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +136,7 @@ LL | w.write(b"hello world").await?; = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:93:9 + --> $DIR/unused_io_amount.rs:101:9 | LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +144,7 @@ LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:101:5 + --> $DIR/unused_io_amount.rs:109:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +152,12 @@ LL | w.write(b"hello world").await.unwrap(); = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:106:5 + --> $DIR/unused_io_amount.rs:114:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: aborting due to 16 previous errors +error: aborting due to 20 previous errors From 20cc72e8a8269107f3405a24861ea7f9d2d748dc Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Wed, 25 Jan 2023 07:07:10 +0100 Subject: [PATCH 487/524] Improve span for module_name_repetitions --- clippy_lints/src/enum_variants.rs | 4 ++-- tests/ui/module_name_repetitions.stderr | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index b77b5621b4c6..4c69dacf381a 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -277,7 +277,7 @@ impl LateLintPass<'_> for EnumVariantNames { Some(c) if is_word_beginning(c) => span_lint( cx, MODULE_NAME_REPETITIONS, - item.span, + item.ident.span, "item name starts with its containing module's name", ), _ => (), @@ -287,7 +287,7 @@ impl LateLintPass<'_> for EnumVariantNames { span_lint( cx, MODULE_NAME_REPETITIONS, - item.span, + item.ident.span, "item name ends with its containing module's name", ); } diff --git a/tests/ui/module_name_repetitions.stderr b/tests/ui/module_name_repetitions.stderr index 3f343a3e4301..277801194a1d 100644 --- a/tests/ui/module_name_repetitions.stderr +++ b/tests/ui/module_name_repetitions.stderr @@ -1,34 +1,34 @@ error: item name starts with its containing module's name - --> $DIR/module_name_repetitions.rs:8:5 + --> $DIR/module_name_repetitions.rs:8:12 | LL | pub fn foo_bar() {} - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ | = note: `-D clippy::module-name-repetitions` implied by `-D warnings` error: item name ends with its containing module's name - --> $DIR/module_name_repetitions.rs:9:5 + --> $DIR/module_name_repetitions.rs:9:12 | LL | pub fn bar_foo() {} - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: item name starts with its containing module's name - --> $DIR/module_name_repetitions.rs:10:5 + --> $DIR/module_name_repetitions.rs:10:16 | LL | pub struct FooCake; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: item name ends with its containing module's name - --> $DIR/module_name_repetitions.rs:11:5 + --> $DIR/module_name_repetitions.rs:11:14 | LL | pub enum CakeFoo {} - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: item name starts with its containing module's name - --> $DIR/module_name_repetitions.rs:12:5 + --> $DIR/module_name_repetitions.rs:12:16 | LL | pub struct Foo7Bar; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: aborting due to 5 previous errors From 986f40fab0828257016477174f9a6036a4a7f4d3 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 26 Jan 2023 15:30:44 +0000 Subject: [PATCH 488/524] `invalid_regex`: Show full error when string value doesn't match source --- clippy_lints/src/regex.rs | 89 +++++++++++++++++++-------------------- tests/ui/regex.rs | 4 ++ tests/ui/regex.stderr | 66 +++++++++++++++++++++-------- 3 files changed, 97 insertions(+), 62 deletions(-) diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 1fda58fa54de..9e6c6c73d4fe 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -1,5 +1,8 @@ +use std::fmt::Display; + use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::source::snippet_opt; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{LitKind, StrStyle}; @@ -77,13 +80,45 @@ impl<'tcx> LateLintPass<'tcx> for Regex { } } -#[must_use] -fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span { - let offset = u32::from(offset); - let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset); - let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset); - assert!(start <= end); - Span::new(start, end, base.ctxt(), base.parent()) +fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescaped: &str, base: Span, offset: u8) { + let parts: Option<(_, _, &dyn Display)> = match &error { + regex_syntax::Error::Parse(e) => Some((e.span(), e.auxiliary_span(), e.kind())), + regex_syntax::Error::Translate(e) => Some((e.span(), None, e.kind())), + _ => None, + }; + + let convert_span = |regex_span: ®ex_syntax::ast::Span| { + let offset = u32::from(offset); + let start = base.lo() + BytePos(u32::try_from(regex_span.start.offset).expect("offset too large") + offset); + let end = base.lo() + BytePos(u32::try_from(regex_span.end.offset).expect("offset too large") + offset); + + Span::new(start, end, base.ctxt(), base.parent()) + }; + + if let Some((primary, auxiliary, kind)) = parts + && let Some(literal_snippet) = snippet_opt(cx, base) + && let Some(inner) = literal_snippet.get(offset as usize..) + // Only convert to native rustc spans if the parsed regex matches the + // source snippet exactly, to ensure the span offsets are correct + && inner.get(..unescaped.len()) == Some(unescaped) + { + let spans = if let Some(auxiliary) = auxiliary { + vec![convert_span(primary), convert_span(auxiliary)] + } else { + vec![convert_span(primary)] + }; + + span_lint(cx, INVALID_REGEX, spans, &format!("regex syntax error: {kind}")); + } else { + span_lint_and_help( + cx, + INVALID_REGEX, + base, + &error.to_string(), + None, + "consider using a raw string literal: `r\"..\"`", + ); + } } fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option { @@ -155,25 +190,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(regex_syntax::Error::Parse(e)) => { - span_lint( - cx, - INVALID_REGEX, - str_span(expr.span, *e.span(), offset), - &format!("regex syntax error: {}", e.kind()), - ); - }, - Err(regex_syntax::Error::Translate(e)) => { - span_lint( - cx, - INVALID_REGEX, - str_span(expr.span, *e.span(), offset), - &format!("regex syntax error: {}", e.kind()), - ); - }, - Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); - }, + Err(e) => lint_syntax_error(cx, &e, r, expr.span, offset), } } } else if let Some(r) = const_str(cx, expr) { @@ -183,25 +200,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(regex_syntax::Error::Parse(e)) => { - span_lint( - cx, - INVALID_REGEX, - expr.span, - &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), - ); - }, - Err(regex_syntax::Error::Translate(e)) => { - span_lint( - cx, - INVALID_REGEX, - expr.span, - &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), - ); - }, - Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); - }, + Err(e) => span_lint(cx, INVALID_REGEX, expr.span, &e.to_string()), } } } diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index f0e1a8128d7c..ab8ac97a0e70 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -36,6 +36,10 @@ fn syntax_error() { let raw_string_error = Regex::new(r"[...\/...]"); let raw_string_error = Regex::new(r#"[...\/...]"#); + + let escaped_string_span = Regex::new("\\b\\c"); + + let aux_span = Regex::new("(?ixi)"); } fn trivial_regex() { diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr index 2424644c6f6b..c2440f39e0a0 100644 --- a/tests/ui/regex.stderr +++ b/tests/ui/regex.stderr @@ -29,7 +29,10 @@ error: regex syntax error: invalid character class range, the start must be <= t LL | let some_unicode = Regex::new("[é-è]"); | ^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:18:33 | LL | let some_regex = Regex::new(OPENING_PAREN); @@ -43,25 +46,37 @@ LL | let binary_pipe_in_wrong_position = BRegex::new("|"); | = help: the regex is unlikely to be useful as it is -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:21:41 | LL | let some_binary_regex = BRegex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:22:56 | LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:34:37 | LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:35:39 | LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); @@ -79,8 +94,25 @@ error: regex syntax error: unrecognized escape sequence LL | let raw_string_error = Regex::new(r#"[...//...]"#); | ^^ +error: regex parse error: + /b/c + ^^ + error: unrecognized escape sequence + --> $DIR/regex.rs:40:42 + | +LL | let escaped_string_span = Regex::new("/b/c"); + | ^^^^^^^^ + | + = help: consider using a raw string literal: `r".."` + +error: regex syntax error: duplicate flag + --> $DIR/regex.rs:42:34 + | +LL | let aux_span = Regex::new("(?ixi)"); + | ^ ^ + error: trivial regex - --> $DIR/regex.rs:42:33 + --> $DIR/regex.rs:46:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -88,7 +120,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:44:48 + --> $DIR/regex.rs:48:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:46:42 + --> $DIR/regex.rs:50:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -104,7 +136,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:48:40 + --> $DIR/regex.rs:52:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -112,7 +144,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:50:39 + --> $DIR/regex.rs:54:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -120,7 +152,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:52:39 + --> $DIR/regex.rs:56:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -128,7 +160,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:54:40 + --> $DIR/regex.rs:58:40 | LL | let trivial_backslash = Regex::new("a/.b"); | ^^^^^^^ @@ -136,7 +168,7 @@ LL | let trivial_backslash = Regex::new("a/.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:57:36 + --> $DIR/regex.rs:61:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -144,7 +176,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:59:36 + --> $DIR/regex.rs:63:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -152,7 +184,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:61:36 + --> $DIR/regex.rs:65:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -160,12 +192,12 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:63:44 + --> $DIR/regex.rs:67:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ | = help: consider using `str::is_empty` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors From 4441753127733dae478d6d25dc95798eef1f1a71 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 20 Jan 2023 09:54:07 +0100 Subject: [PATCH 489/524] Mark Rust 1.67 as released in the changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3cb880df57b..e2cde09776f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,13 @@ All notable changes to this project will be documented in this file. See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this document. -## Unreleased / In Rust Nightly +## Unreleased / Beta / In Rust Nightly [d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master) ## Rust 1.67 -Current beta, released 2023-01-26 +Current stable, released 2023-01-26 [4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d) @@ -203,7 +203,7 @@ Current beta, released 2023-01-26 ## Rust 1.66 -Current stable, released 2022-12-15 +Released 2022-12-15 [b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1) From a1a01c19f15d597215911e966667e0d35905e967 Mon Sep 17 00:00:00 2001 From: Collin Styles Date: Thu, 26 Jan 2023 16:44:44 -0800 Subject: [PATCH 490/524] Fix docs for `suspicious_xor_used_as_pow` lint There was a tab after the three leading slashes which caused the contents of the "Why is this bad?" section to be rendered as a code block. --- clippy_lints/src/suspicious_xor_used_as_pow.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/suspicious_xor_used_as_pow.rs b/clippy_lints/src/suspicious_xor_used_as_pow.rs index c181919b1647..9c0dc8096d0d 100644 --- a/clippy_lints/src/suspicious_xor_used_as_pow.rs +++ b/clippy_lints/src/suspicious_xor_used_as_pow.rs @@ -9,7 +9,7 @@ declare_clippy_lint! { /// ### What it does /// Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal. /// ### Why is this bad? - /// It's most probably a typo and may lead to unexpected behaviours. + /// It's most probably a typo and may lead to unexpected behaviours. /// ### Example /// ```rust /// let x = 3_i32 ^ 4_i32; From 1f403e9ab96e12f374dfdd9e8967eea940122ce7 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 27 Jan 2023 20:26:48 +0100 Subject: [PATCH 491/524] Bump nightly version -> 2023-01-27 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 40a6f47095ec..4e7fc565a322 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-01-12" +channel = "nightly-2023-01-27" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From 6f9c70a2015aadd1dc1b77d1e988217aeebd75c5 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Fri, 27 Jan 2023 20:27:00 +0100 Subject: [PATCH 492/524] Bump Clippy version -> 0.1.69 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e1b15cc49da4..dc94b1045249 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.68" +version = "0.1.69" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 38a87017635b..7278ad13d568 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.68" +version = "0.1.69" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index ac6a566b9cd3..173469f6cdc7 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.68" +version = "0.1.69" edition = "2021" publish = false diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index c01e1062cb54..80eee368178e 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.68" +version = "0.1.69" edition = "2021" publish = false From a0460cf37d010643c1c7fa1c26923c3d61075e3f Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 01:49:10 +0900 Subject: [PATCH 493/524] add COLLAPSIBLE_STR_REPLACE in msrv COLLAPSIBLE_STR_REPLACE uses msrvs::PATTERN_TRAIT_CHAR_ARRAY --- book/src/lint_configuration.md | 1 + clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index f79dbb50ff49..6068b4aa9504 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -167,6 +167,7 @@ The minimum rust version that the project supports * [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) * [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) +* [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index f48be27592b7..e81e983e3c1f 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From 5a9c4a009052ad5e5baf6f9ee8450136e1bde939 Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 01:52:05 +0900 Subject: [PATCH 494/524] add SEEK_FROM_CURRENT in msrv SEEK_FROM_CURRENT uses msrvs::SEEK_FROM_CURRENT --- book/src/lint_configuration.md | 1 + clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 6068b4aa9504..7c8a3291f942 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -168,6 +168,7 @@ The minimum rust version that the project supports * [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) * [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) +* [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index e81e983e3c1f..36ca69c23f74 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From a05e86f5ddcc976ac1d44692bafd92dfd65b62a6 Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 01:54:31 +0900 Subject: [PATCH 495/524] add SEEK_REWIND in msrv SEEK_REWIND uses msrvs::SEEK_REWIND --- book/src/lint_configuration.md | 1 + clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 7c8a3291f942..4c204d93e0af 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -169,6 +169,7 @@ The minimum rust version that the project supports * [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) * [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) * [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) +* [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 36ca69c23f74..33ca3c16a24c 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From 532841fcae289ff328ddbb10587695b2c6965771 Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 02:02:46 +0900 Subject: [PATCH 496/524] add UNNECESSARY_LAZY_EVALUATIONS to msrv UNNECESSARY_LAZY_EVALUATIONS uses msrvs::BOOL_THEN_SOME for `then` to `then_some` --- book/src/lint_configuration.md | 1 + clippy_lints/src/methods/mod.rs | 1 + clippy_lints/src/utils/conf.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 4c204d93e0af..d24b5259a0b8 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -170,6 +170,7 @@ The minimum rust version that the project supports * [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) * [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) * [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) +* [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 42377a3d138c..6a1575e182f0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1818,6 +1818,7 @@ declare_clippy_lint! { /// - `or_else` to `or` /// - `get_or_insert_with` to `get_or_insert` /// - `ok_or_else` to `ok_or` + /// - `then` to `then_some` (for msrv >= 1.62.0) /// /// ### Why is this bad? /// Using eager evaluation is shorter and simpler in some cases. diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 33ca3c16a24c..21301e2e31b0 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From 07a8bf15ff4fa044670bd9e6d5b823bfff84f1a9 Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 02:14:38 +0900 Subject: [PATCH 497/524] add TRANSMUTE_PTR_TO_REF to msrv TRANSMUTE_PTR_TO_REF uses msrvs::POINTER_CAST --- book/src/lint_configuration.md | 1 + clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index d24b5259a0b8..c621dec1d607 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -171,6 +171,7 @@ The minimum rust version that the project supports * [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) * [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) * [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) +* [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 21301e2e31b0..5e7ae71ea362 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From e65f9f9d32e53ce4edbd1bee96f4e5cdf67e1624 Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 02:22:10 +0900 Subject: [PATCH 498/524] add ALMOST_COMPLETE_RANGE to msrv ALMOST_COMPLETE_RANGE uses msrvs::RANGE_INCLUSIVE --- book/src/lint_configuration.md | 1 + clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index c621dec1d607..9b4460fc6556 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -172,6 +172,7 @@ The minimum rust version that the project supports * [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) * [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) * [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) +* [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 5e7ae71ea362..c7aec62c1eec 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From 7716d69757ec65e11332ea222e1c83aafd1f53db Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 02:27:43 +0900 Subject: [PATCH 499/524] fix: add missing dot to AWAIT_HOLDING_INVALID_TYPE --- book/src/lint_configuration.md | 9 +++++++++ clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 9b4460fc6556..d425b46be51a 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -43,6 +43,7 @@ Please use that command to update the file and do not edit it by hand. | [allowed-scripts](#allowed-scripts) | `["Latin"]` | | [enable-raw-pointer-heuristic-for-send](#enable-raw-pointer-heuristic-for-send) | `true` | | [max-suggested-slice-pattern-length](#max-suggested-slice-pattern-length) | `3` | +| [await-holding-invalid-types](#await-holding-invalid-types) | `[]` | | [max-include-file-size](#max-include-file-size) | `1000000` | | [allow-expect-in-tests](#allow-expect-in-tests) | `false` | | [allow-unwrap-in-tests](#allow-unwrap-in-tests) | `false` | @@ -448,6 +449,14 @@ For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. * [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +### await-holding-invalid-types + + +**Default Value:** `[]` (`Vec`) + +* [await_holding_invalid_type](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type) + + ### max-include-file-size The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index c7aec62c1eec..1b0aa9a63eda 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -411,7 +411,7 @@ define_Conf! { /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), - /// Lint: AWAIT_HOLDING_INVALID_TYPE + /// Lint: AWAIT_HOLDING_INVALID_TYPE. (await_holding_invalid_types: Vec = Vec::new()), /// Lint: LARGE_INCLUDE_FILE. /// From fb77b027891daf4c730e3f80852ce446fef9beab Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 02:37:00 +0900 Subject: [PATCH 500/524] add NEEDLESS_BORROW to msrv NEEDLESS_BORROW uses msrvs::ARRAY_INTO_ITERATOR --- book/src/lint_configuration.md | 1 + clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index d425b46be51a..9bd4e72fe8f3 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -174,6 +174,7 @@ The minimum rust version that the project supports * [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) * [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) +* [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 1b0aa9a63eda..7364729606b3 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From e791522d35bf33a9008c367902ccaf3387325968 Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 02:39:37 +0900 Subject: [PATCH 501/524] add DERIVABLE_IMPLS to msrv DERIVABLE_IMPLS uses msrvs::DEFAULT_ENUM_ATTRIBUTE --- book/src/lint_configuration.md | 1 + clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 9bd4e72fe8f3..9f7cf9a61637 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -175,6 +175,7 @@ The minimum rust version that the project supports * [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) * [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) +* [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 7364729606b3..f7a35dc798ee 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From 25d455bd1728ba28c5cf6a0e044f2194a7d4d2ca Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 02:54:26 +0900 Subject: [PATCH 502/524] fix: add missing dot to suppress_restriction_lint_in_const --- book/src/lint_configuration.md | 2 +- clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 9f7cf9a61637..32a2b44392d4 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -526,7 +526,7 @@ Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar) ### suppress-restriction-lint-in-const -In same +Whether to suppress a restriction lint in constant code. In same cases the restructured operation might not be unavoidable, as the suggested counterparts are unavailable in constant code. This configuration will cause restriction lints to trigger even diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index f7a35dc798ee..f8a5b5547ded 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -446,7 +446,7 @@ define_Conf! { /// /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` (allow_mixed_uninlined_format_args: bool = true), - /// Lint: INDEXING_SLICING + /// Lint: INDEXING_SLICING. /// /// Whether to suppress a restriction lint in constant code. In same /// cases the restructured operation might not be unavoidable, as the From 1766532b20a7af11cd4cd52545a643b50a117040 Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 03:02:23 +0900 Subject: [PATCH 503/524] add MANUAL_IS_ASCII_CHECK to msrv MANUAL_IS_ASCII_CHECK uses msrvs::IS_ASCII_DIGIT and msrvs::IS_ASCII_DIGIT_CONST --- book/src/lint_configuration.md | 1 + clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 32a2b44392d4..07414f5408b0 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -176,6 +176,7 @@ The minimum rust version that the project supports * [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) * [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) * [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) +* [manual_is_ascii_check](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index f8a5b5547ded..380884cc21e7 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From 2f4b047b277b70ff0b43e07e00e58d54d92fda3c Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 03:04:56 +0900 Subject: [PATCH 504/524] add MANUAL_REM_EUCLID to msrv MANUAL_REM_EUCLID uses msrvs::REM_EUCLID --- book/src/lint_configuration.md | 1 + clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 07414f5408b0..78304ca48fe0 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -177,6 +177,7 @@ The minimum rust version that the project supports * [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) * [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) * [manual_is_ascii_check](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) +* [manual_rem_euclid](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 380884cc21e7..e47546e3e429 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From d87a6bc9b18594bdadb479f7ade1361527d3f46b Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 03:07:09 +0900 Subject: [PATCH 505/524] add MANUAL_RETAIN to msrv MANUAL_RETAIN uses - msrvs::STRING_RETAIN - msrvs::BTREE_SET_RETAIN - msrvs::BTREE_MAP_RETAIN - msrvs::HASH_SET_RETAIN - msrvs::HASH_MAP_RETAIN --- book/src/lint_configuration.md | 1 + clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 78304ca48fe0..1db2ad5859cb 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -178,6 +178,7 @@ The minimum rust version that the project supports * [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) * [manual_is_ascii_check](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) * [manual_rem_euclid](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) +* [manual_retain](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) ### cognitive-complexity-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index e47546e3e429..5bdffab26b22 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -253,7 +253,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, 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. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, 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. /// /// The minimum rust version that the project supports (msrv: Option = None), From af62bf95a3fc2d8d1164698160507fa7d7035284 Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 03:13:30 +0900 Subject: [PATCH 506/524] fix key name of MUTABLE_KEY_TYPE --- book/src/lint_configuration.md | 2 +- clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 1db2ad5859cb..9acacede5a88 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -517,7 +517,7 @@ for the generic parameters for determining interior mutability **Default Value:** `["bytes::Bytes"]` (`Vec`) -* [mutable_key](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key) +* [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) ### allow-mixed-uninlined-format-args diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 5bdffab26b22..4dfe23f474f6 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -437,7 +437,7 @@ define_Conf! { /// /// The maximum size of the `Err`-variant in a `Result` returned from a function (large_error_threshold: u64 = 128), - /// Lint: MUTABLE_KEY. + /// Lint: MUTABLE_KEY_TYPE. /// /// A list of paths to types that should be treated like `Arc`, i.e. ignored but /// for the generic parameters for determining interior mutability From 4d266d31ded825d43df64e496cea54bdcf2f65e4 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Sun, 29 Jan 2023 11:41:53 -0700 Subject: [PATCH 507/524] needless_range_loop: improve documentation --- clippy_lints/src/loops/mod.rs | 3 ++- clippy_lints/src/loops/needless_range_loop.rs | 2 +- tests/ui/needless_range_loop.stderr | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 8e52cac4323c..610a0233eee1 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -61,7 +61,8 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Just iterating the collection itself makes the intent - /// more clear and is probably faster. + /// more clear and is probably faster because it eliminates + /// the bounds check that is done when indexing. /// /// ### Example /// ```rust diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 3bca93d80aa7..1336b80d88d6 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -149,7 +149,7 @@ pub(super) fn check<'tcx>( |diag| { multispan_sugg( diag, - "consider using an iterator", + "consider using an iterator and enumerate", vec![ (pat.span, format!("({}, )", ident.name)), ( diff --git a/tests/ui/needless_range_loop.stderr b/tests/ui/needless_range_loop.stderr index b31544ec334a..81b6f214bd11 100644 --- a/tests/ui/needless_range_loop.stderr +++ b/tests/ui/needless_range_loop.stderr @@ -49,7 +49,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate | LL | for (i, ) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ @@ -126,7 +126,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate | LL | for (i, ) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -137,7 +137,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..10 { | ^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate | LL | for (i, ) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -148,7 +148,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate | LL | for (i, ) in vec.iter_mut().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~ From 2fd94a4e01d9e8049df3651d0b29b3964cf235d8 Mon Sep 17 00:00:00 2001 From: ksaleem Date: Sun, 29 Jan 2023 13:48:06 -0500 Subject: [PATCH 508/524] prevents `len_without_is_empty` from yielding positive when `len` takes more than just `&self` in non-standard implementations. changelog: Fix [`len_without_is_empty`] false positive when len has a non-standard method signature Fixes #9520 --- clippy_lints/src/len_zero.rs | 1 + tests/ui/len_without_is_empty.rs | 46 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 3c70c9cf19a5..920ab7f06336 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -135,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if item.ident.name == sym::len; if let ImplItemKind::Fn(sig, _) = &item.kind; if sig.decl.implicit_self.has_implicit_self(); + if sig.decl.inputs.len() == 1; if cx.effective_visibilities.is_exported(item.owner_id.def_id); if matches!(sig.decl.output, FnRetTy::Return(_)); if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()); diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index 78397c2af346..b5dec6c46bdd 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -282,4 +282,50 @@ impl AsyncLen { } } +// issue #9520 +pub struct NonStandardLenAndIsEmptySignature; +impl NonStandardLenAndIsEmptySignature { + // don't lint + pub fn len(&self, something: usize) -> usize { + something + } + + pub fn is_empty(&self, something: usize) -> bool { + something == 0 + } +} + +// test case for #9520 with generics in the function signature +pub trait TestResource { + type NonStandardSignatureWithGenerics: Copy; + fn lookup_content(&self, item: Self::NonStandardSignatureWithGenerics) -> Result, String>; +} +pub struct NonStandardSignatureWithGenerics(u32); +impl NonStandardSignatureWithGenerics { + pub fn is_empty(self, resource: &T) -> bool + where + T: TestResource, + U: Copy + From, + { + if let Ok(Some(content)) = resource.lookup_content(self.into()) { + content.is_empty() + } else { + true + } + } + + // test case for #9520 with generics in the function signature + pub fn len(self, resource: &T) -> usize + where + T: TestResource, + U: Copy + From, + { + if let Ok(Some(content)) = resource.lookup_content(self.into()) { + content.len() + } else { + 0_usize + } + } +} + fn main() {} From ecde2019e9ad63282b647fbce33cbbc3d394fd84 Mon Sep 17 00:00:00 2001 From: Sylvain Desodt Date: Mon, 30 Jan 2023 10:01:29 +0100 Subject: [PATCH 509/524] Fix version declared for semicolon_inside_block and semicolon_outside_block As per Issue #10244, the lint were documentated as being part of 1.66.0 but will actually be released 1.68.0 . --- clippy_lints/src/semicolon_block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 8f1d1490e1f0..34a3e5ddf4f6 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// # let x = 0; /// unsafe { f(x); } /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.68.0"] pub SEMICOLON_INSIDE_BLOCK, restriction, "add a semicolon inside the block" @@ -59,7 +59,7 @@ declare_clippy_lint! { /// # let x = 0; /// unsafe { f(x) }; /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.68.0"] pub SEMICOLON_OUTSIDE_BLOCK, restriction, "add a semicolon outside the block" From a9e6b128542134c340bb25e13144eafbd176f51b Mon Sep 17 00:00:00 2001 From: koka Date: Sun, 29 Jan 2023 03:22:17 +0900 Subject: [PATCH 510/524] fix: use correct lint name fix --- book/src/lint_configuration.md | 2 +- clippy_lints/src/utils/conf.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 9acacede5a88..32e8e218c405 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -291,7 +291,7 @@ The minimum size (in bytes) to consider a type for passing by reference instead **Default Value:** `256` (`u64`) -* [large_type_pass_by_move](https://rust-lang.github.io/rust-clippy/master/index.html#large_type_pass_by_move) +* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) ### too-many-lines-threshold diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 4dfe23f474f6..1d78c7cfae0d 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -323,7 +323,7 @@ define_Conf! { /// /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. (trivial_copy_size_limit: Option = None), - /// Lint: LARGE_TYPE_PASS_BY_MOVE. + /// Lint: LARGE_TYPES_PASSED_BY_VALUE. /// /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. (pass_by_value_size_limit: u64 = 256), From 3b225e3a96abcf8eab678691b244e5cdda4c6ea5 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Mon, 30 Jan 2023 09:59:26 -0700 Subject: [PATCH 511/524] Update clippy_lints/src/loops/needless_range_loop.rs Co-authored-by: Manish Goregaokar --- clippy_lints/src/loops/needless_range_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 1336b80d88d6..d060b6ade24e 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -149,7 +149,7 @@ pub(super) fn check<'tcx>( |diag| { multispan_sugg( diag, - "consider using an iterator and enumerate", + "consider using an iterator and enumerate()", vec![ (pat.span, format!("({}, )", ident.name)), ( From 5ed191de6bf14cdc0aee749fd03fb6dfb32c4926 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Mon, 30 Jan 2023 10:10:52 -0700 Subject: [PATCH 512/524] bless --- tests/ui/needless_range_loop.stderr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ui/needless_range_loop.stderr b/tests/ui/needless_range_loop.stderr index 81b6f214bd11..cffa19bec3a6 100644 --- a/tests/ui/needless_range_loop.stderr +++ b/tests/ui/needless_range_loop.stderr @@ -49,7 +49,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator and enumerate +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ @@ -126,7 +126,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator and enumerate +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -137,7 +137,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..10 { | ^^^^^ | -help: consider using an iterator and enumerate +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -148,7 +148,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator and enumerate +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter_mut().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~ From 926c5e4cde09470ecfcdae562e802e5abbddb17c Mon Sep 17 00:00:00 2001 From: Niki4tap Date: Mon, 30 Jan 2023 20:42:40 +0300 Subject: [PATCH 513/524] multiple_unsafe_ops_per_block: don't lint in external macros --- .../src/multiple_unsafe_ops_per_block.rs | 3 +- tests/ui/auxiliary/macro_rules.rs | 10 +++++ tests/ui/multiple_unsafe_ops_per_block.rs | 9 +++++ tests/ui/multiple_unsafe_ops_per_block.stderr | 40 +++++++++---------- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 18e61c75eece..191da3085be2 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -10,6 +10,7 @@ use hir::{ use rustc_ast::Mutability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -66,7 +67,7 @@ declare_lint_pass!(MultipleUnsafeOpsPerBlock => [MULTIPLE_UNSAFE_OPS_PER_BLOCK]) impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) { + if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) || in_external_macro(cx.tcx.sess, block.span) { return; } let mut unsafe_ops = vec![]; diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 1e5f20e8c39b..f74a614eefe9 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -149,3 +149,13 @@ macro_rules! almost_complete_range { let _ = '0'..'9'; }; } + +#[macro_export] +macro_rules! unsafe_macro { + () => { + unsafe { + *core::ptr::null::<()>(); + *core::ptr::null::<()>(); + } + }; +} diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 41263535df67..4511bc99c3c7 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,9 +1,13 @@ +// aux-build:macro_rules.rs #![allow(unused)] #![allow(deref_nullptr)] #![allow(clippy::unnecessary_operation)] #![allow(clippy::drop_copy)] #![warn(clippy::multiple_unsafe_ops_per_block)] +#[macro_use] +extern crate macro_rules; + use core::arch::asm; fn raw_ptr() -> *const () { @@ -107,4 +111,9 @@ unsafe fn read_char_good(ptr: *const u8) -> char { unsafe { core::char::from_u32_unchecked(int_value) } } +// no lint +fn issue10259() { + unsafe_macro!(); +} + fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index f6b8341795d2..303aeb7aee0c 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -1,5 +1,5 @@ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:32:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:36:5 | LL | / unsafe { LL | | STATIC += 1; @@ -8,19 +8,19 @@ LL | | } | |_____^ | note: modification of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:33:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:37:9 | LL | STATIC += 1; | ^^^^^^^^^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:34:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:38:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ = note: `-D clippy::multiple-unsafe-ops-per-block` implied by `-D warnings` error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:41:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:45:5 | LL | / unsafe { LL | | drop(u.u); @@ -29,18 +29,18 @@ LL | | } | |_____^ | note: union field access occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:42:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:46:14 | LL | drop(u.u); | ^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:43:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:47:9 | LL | *raw_ptr(); | ^^^^^^^^^^ error: this `unsafe` block contains 3 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:48:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:52:5 | LL | / unsafe { LL | | asm!("nop"); @@ -50,23 +50,23 @@ LL | | } | |_____^ | note: inline assembly used here - --> $DIR/multiple_unsafe_ops_per_block.rs:49:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:53:9 | LL | asm!("nop"); | ^^^^^^^^^^^ note: unsafe method call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:50:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:54:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: modification of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:51:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:55:9 | LL | STATIC = 0; | ^^^^^^^^^^ error: this `unsafe` block contains 6 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:57:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:61:5 | LL | / unsafe { LL | | drop(u.u); @@ -78,49 +78,49 @@ LL | | } | |_____^ | note: union field access occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:58:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:62:14 | LL | drop(u.u); | ^^^ note: access of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:59:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:63:14 | LL | drop(STATIC); | ^^^^^^ note: unsafe method call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:60:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:64:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:61:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:65:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:62:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:66:9 | LL | *raw_ptr(); | ^^^^^^^^^^ note: inline assembly used here - --> $DIR/multiple_unsafe_ops_per_block.rs:63:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:67:9 | LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:101:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:5 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:101:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:14 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:101:39 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:39 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^ From c959813bfd376322491f0a7b89d7dce79d5af942 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Mon, 30 Jan 2023 07:59:01 -0700 Subject: [PATCH 514/524] needless_lifetimes: macro test Signed-off-by: Tyler Weaver --- tests/ui/needless_lifetimes.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 2efc936752ef..ac467cbbdf1e 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -495,4 +495,17 @@ mod pr_9743_output_lifetime_checks { } } +mod skip_inside_macros { + macro_rules! print_with_one_input { + ($a:expr) => { + fn print_with_one_input<'a>(x: &'a u8) -> &'a u8 { + println!("{}", $a); + unimplemented!() + } + }; + } + + print_with_one_input!("this is a dandy little string literal"); +} + fn main() {} From 4fde96c30eab3845905f8eb0b074c1168877dd95 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Mon, 30 Jan 2023 16:04:19 -0700 Subject: [PATCH 515/524] Test needless_lifetimes within external macro Signed-off-by: Tyler Weaver --- clippy_lints/src/lifetimes.rs | 5 +- tests/ui/auxiliary/macro_rules.rs | 9 +++ tests/ui/needless_lifetimes.rs | 19 +++-- tests/ui/needless_lifetimes.stderr | 117 ++++++++++++++++------------- 4 files changed, 89 insertions(+), 61 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 7cf1a6b8084a..ef9ac96ace5c 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -13,8 +13,9 @@ use rustc_hir::{ ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter as middle_nested_filter; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; @@ -144,7 +145,7 @@ fn check_fn_inner<'tcx>( span: Span, report_extra_lifetimes: bool, ) { - if span.from_expansion() || has_where_lifetimes(cx, generics) { + if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) { return; } diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index f74a614eefe9..a13af5652038 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -159,3 +159,12 @@ macro_rules! unsafe_macro { } }; } + +#[macro_export] +macro_rules! needless_lifetime { + () => { + fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + }; +} diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index ac467cbbdf1e..78493c6d0672 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,3 +1,4 @@ +// aux-build:macro_rules.rs #![warn(clippy::needless_lifetimes)] #![allow( dead_code, @@ -8,6 +9,9 @@ clippy::get_first )] +#[macro_use] +extern crate macro_rules; + fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} @@ -495,17 +499,20 @@ mod pr_9743_output_lifetime_checks { } } -mod skip_inside_macros { - macro_rules! print_with_one_input { - ($a:expr) => { - fn print_with_one_input<'a>(x: &'a u8) -> &'a u8 { - println!("{}", $a); +mod in_macro { + macro_rules! local_one_input_macro { + () => { + fn one_input<'a>(x: &'a u8) -> &'a u8 { unimplemented!() } }; } - print_with_one_input!("this is a dandy little string literal"); + // lint local macro expands to function with needless lifetimes + local_one_input_macro!(); + + // no lint on external macro + macro_rules::needless_lifetime!(); } fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index 5a7cf13c86dd..9d02626956e0 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,5 +1,5 @@ error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:11:1 + --> $DIR/needless_lifetimes.rs:15:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,310 +7,321 @@ LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} = note: `-D clippy::needless-lifetimes` implied by `-D warnings` error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:13:1 + --> $DIR/needless_lifetimes.rs:17:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:23:1 + --> $DIR/needless_lifetimes.rs:27:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:35:1 + --> $DIR/needless_lifetimes.rs:39:1 | LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:42:1 + --> $DIR/needless_lifetimes.rs:46:1 | LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:59:1 + --> $DIR/needless_lifetimes.rs:63:1 | LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:66:1 + --> $DIR/needless_lifetimes.rs:70:1 | LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:75:1 + --> $DIR/needless_lifetimes.rs:79:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:80:1 + --> $DIR/needless_lifetimes.rs:84:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:92:1 + --> $DIR/needless_lifetimes.rs:96:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:92:37 + --> $DIR/needless_lifetimes.rs:96:37 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:116:1 + --> $DIR/needless_lifetimes.rs:120:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:116:32 + --> $DIR/needless_lifetimes.rs:120:32 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^ error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:146:5 + --> $DIR/needless_lifetimes.rs:150:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 't - --> $DIR/needless_lifetimes.rs:153:5 + --> $DIR/needless_lifetimes.rs:157:5 | LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:160:5 + --> $DIR/needless_lifetimes.rs:164:5 | LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 's, 't - --> $DIR/needless_lifetimes.rs:164:5 + --> $DIR/needless_lifetimes.rs:168:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:183:1 + --> $DIR/needless_lifetimes.rs:187:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:183:33 + --> $DIR/needless_lifetimes.rs:187:33 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:201:1 + --> $DIR/needless_lifetimes.rs:205:1 | LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:201:43 + --> $DIR/needless_lifetimes.rs:205:43 | LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:209:1 + --> $DIR/needless_lifetimes.rs:213:1 | LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:224:1 + --> $DIR/needless_lifetimes.rs:228:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:230:1 + --> $DIR/needless_lifetimes.rs:234:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:230:37 + --> $DIR/needless_lifetimes.rs:234:37 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:248:1 + --> $DIR/needless_lifetimes.rs:252:1 | LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:248:47 + --> $DIR/needless_lifetimes.rs:252:47 | LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:256:1 + --> $DIR/needless_lifetimes.rs:260:1 | LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:260:1 + --> $DIR/needless_lifetimes.rs:264:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:268:1 + --> $DIR/needless_lifetimes.rs:272:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:304:1 + --> $DIR/needless_lifetimes.rs:308:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:304:47 + --> $DIR/needless_lifetimes.rs:308:47 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:311:9 + --> $DIR/needless_lifetimes.rs:315:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:315:9 + --> $DIR/needless_lifetimes.rs:319:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:328:9 + --> $DIR/needless_lifetimes.rs:332:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:360:5 + --> $DIR/needless_lifetimes.rs:364:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:369:5 + --> $DIR/needless_lifetimes.rs:373:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:381:5 + --> $DIR/needless_lifetimes.rs:385:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:396:5 + --> $DIR/needless_lifetimes.rs:400:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:409:5 + --> $DIR/needless_lifetimes.rs:413:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:412:5 + --> $DIR/needless_lifetimes.rs:416:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:434:9 + --> $DIR/needless_lifetimes.rs:438:9 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:437:9 + --> $DIR/needless_lifetimes.rs:441:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:448:9 + --> $DIR/needless_lifetimes.rs:452:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:454:9 + --> $DIR/needless_lifetimes.rs:458:9 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:455:9 + --> $DIR/needless_lifetimes.rs:459:9 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:464:9 + --> $DIR/needless_lifetimes.rs:468:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:465:9 + --> $DIR/needless_lifetimes.rs:469:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:474:5 + --> $DIR/needless_lifetimes.rs:478:5 | LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:476:5 + --> $DIR/needless_lifetimes.rs:480:5 | LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:483:5 + --> $DIR/needless_lifetimes.rs:487:5 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:488:5 + --> $DIR/needless_lifetimes.rs:492:5 | LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 45 previous errors +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:505:13 + | +LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | local_one_input_macro!(); + | ------------------------ in this macro invocation + | + = note: this error originates in the macro `local_one_input_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 46 previous errors From 2432e97d6ab7e44390542c00229344bf45fcaddd Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Sat, 28 Jan 2023 18:45:57 -0700 Subject: [PATCH 516/524] wildcard_enum_match_arm lint takes the enum origin into account Signed-off-by: Tyler Weaver --- clippy_lints/src/matches/match_wild_enum.rs | 17 +++++++++++------ .../ui/match_wildcard_for_single_variants.fixed | 2 +- .../match_wildcard_for_single_variants.stderr | 8 +++++++- tests/ui/wildcard_enum_match_arm.fixed | 2 +- tests/ui/wildcard_enum_match_arm.stderr | 4 ++-- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 59de8c0384ba..05cac0f99797 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -45,8 +45,12 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let has_hidden = adt_def.variants().iter().any(|x| is_hidden(cx, x)); - let mut missing_variants: Vec<_> = adt_def.variants().iter().filter(|x| !is_hidden(cx, x)).collect(); + let has_hidden_external = adt_def.variants().iter().any(|x| is_hidden_and_external(cx, x)); + let mut missing_variants: Vec<_> = adt_def + .variants() + .iter() + .filter(|x| !is_hidden_and_external(cx, x)) + .collect(); let mut path_prefix = CommonPrefixSearcher::None; for arm in arms { @@ -133,7 +137,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden_external => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, @@ -144,7 +148,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ), variants => { let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); - let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden { + let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden_external { suggestions.push("_".into()); "wildcard matches known variants and will also match future added variants" } else { @@ -191,6 +195,7 @@ impl<'a> CommonPrefixSearcher<'a> { } } -fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { - cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable) +fn is_hidden_and_external(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { + (cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable)) + && variant_def.def_id.as_local().is_none() } diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index fc252cdd3529..9fd3739b69c2 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -123,7 +123,7 @@ fn main() { Enum::A => (), Enum::B => (), Enum::C => (), - _ => (), + Enum::__Private => (), } match Enum::A { Enum::A => (), diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 6fa313dc9111..105b4c4b41d1 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -48,11 +48,17 @@ error: wildcard matches only a single variant and will also match any future add LL | _ => (), | ^ help: try this: `Color::Blue` +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:126:13 + | +LL | _ => (), + | ^ help: try this: `Enum::__Private` + error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:153:13 | LL | _ => 2, | ^ help: try this: `Foo::B` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index 23607497841e..293bf75a7176 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -96,7 +96,7 @@ fn main() { } match Enum::A { Enum::A => (), - Enum::B | _ => (), + Enum::B | Enum::__Private => (), } } } diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index efecc9576cc7..30d29aa4e77a 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -34,11 +34,11 @@ error: wildcard matches known variants and will also match future added variants LL | _ => {}, | ^ help: try this: `ErrorKind::PermissionDenied | _` -error: wildcard matches known variants and will also match future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:99:13 | LL | _ => (), - | ^ help: try this: `Enum::B | _` + | ^ help: try this: `Enum::B | Enum::__Private` error: aborting due to 6 previous errors From c531b09eb85e4dff966b6bfb93e7fbf128fdfba0 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Mon, 30 Jan 2023 15:46:34 -0700 Subject: [PATCH 517/524] Check external before hidden --- clippy_lints/src/matches/match_wild_enum.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 05cac0f99797..3b35c04620c1 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -3,6 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::{Arm, Expr, PatKind, PathSegment, QPath, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, VariantDef}; @@ -45,11 +46,11 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let has_hidden_external = adt_def.variants().iter().any(|x| is_hidden_and_external(cx, x)); + let has_hidden_external = adt_def.variants().iter().any(|x| is_external_and_hidden(cx, x)); let mut missing_variants: Vec<_> = adt_def .variants() .iter() - .filter(|x| !is_hidden_and_external(cx, x)) + .filter(|x| !is_external_and_hidden(cx, x)) .collect(); let mut path_prefix = CommonPrefixSearcher::None; @@ -195,7 +196,14 @@ impl<'a> CommonPrefixSearcher<'a> { } } -fn is_hidden_and_external(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { - (cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable)) - && variant_def.def_id.as_local().is_none() +fn is_external_and_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { + is_external(variant_def.def_id) && is_hidden(cx, variant_def) +} + +fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { + cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable) +} + +fn is_external(def_id: DefId) -> bool { + def_id.as_local().is_none() } From df7cdf732d3679f21c4a36808fb43b832f0a6725 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Mon, 30 Jan 2023 17:29:17 -0700 Subject: [PATCH 518/524] Pull the is_external test out of the loop --- clippy_lints/src/matches/match_wild_enum.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 3b35c04620c1..3126b590180e 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -3,7 +3,6 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; use rustc_hir::{Arm, Expr, PatKind, PathSegment, QPath, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, VariantDef}; @@ -46,11 +45,12 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let has_hidden_external = adt_def.variants().iter().any(|x| is_external_and_hidden(cx, x)); + let is_external = adt_def.did().as_local().is_none(); + let has_external_hidden = is_external && adt_def.variants().iter().any(|x| is_hidden(cx, x)); let mut missing_variants: Vec<_> = adt_def .variants() .iter() - .filter(|x| !is_external_and_hidden(cx, x)) + .filter(|x| !(is_external && is_hidden(cx, x))) .collect(); let mut path_prefix = CommonPrefixSearcher::None; @@ -138,7 +138,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden_external => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() && !has_external_hidden => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, @@ -149,7 +149,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ), variants => { let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); - let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden_external { + let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden { suggestions.push("_".into()); "wildcard matches known variants and will also match future added variants" } else { @@ -196,14 +196,6 @@ impl<'a> CommonPrefixSearcher<'a> { } } -fn is_external_and_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { - is_external(variant_def.def_id) && is_hidden(cx, variant_def) -} - fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable) } - -fn is_external(def_id: DefId) -> bool { - def_id.as_local().is_none() -} From b4e2b48270008b2c4fa84f4d6270e13a93f1ac14 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 30 Jan 2023 19:28:27 -0800 Subject: [PATCH 519/524] Mark uninlined_format_args as pedantic --- clippy_lints/src/format_args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index ab05d4688ccf..d6b02f174f93 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -125,7 +125,7 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.66.0"] pub UNINLINED_FORMAT_ARGS, - style, + pedantic, "using non-inlined variables in `format!` calls" } From 6a8b20230b4e1ccdc5936d9cde5a6018d3b8fc74 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 31 Jan 2023 14:12:03 +0000 Subject: [PATCH 520/524] Add machine applicable suggestion for `needless_lifetimes` Also adds a test for #5787 --- clippy_lints/src/lifetimes.rs | 253 +++++---- tests/ui/crashes/ice-2774.stderr | 5 + .../needless_lifetimes_impl_trait.stderr | 5 + tests/ui/needless_lifetimes.fixed | 537 ++++++++++++++++++ tests/ui/needless_lifetimes.rs | 21 +- tests/ui/needless_lifetimes.stderr | 380 ++++++++++--- 6 files changed, 1019 insertions(+), 182 deletions(-) create mode 100644 tests/ui/needless_lifetimes.fixed diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index ef9ac96ace5c..3cccc2cfe2aa 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,22 +1,21 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; -use rustc_hir::lang_items; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, PolyTraitRef, PredicateOrigin, TraitFn, - TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + lang_items, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, + Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, + PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -35,8 +34,6 @@ declare_clippy_lint! { /// ### Known problems /// - We bail out if the function has a `where` clause where lifetimes /// are mentioned due to potential false positives. - /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the - /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. /// /// ### Example /// ```rust @@ -94,7 +91,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, generics, id) = item.kind { - check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); + check_fn_inner(cx, sig, Some(id), None, generics, item.span, true); } else if let ItemKind::Impl(impl_) = item.kind { if !item.span.from_expansion() { report_extra_impl_lifetimes(cx, impl_); @@ -107,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none(); check_fn_inner( cx, - sig.decl, + sig, Some(id), None, item.generics, @@ -123,22 +120,14 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true); + check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true); } } } -/// The lifetime of a &-reference. -#[derive(PartialEq, Eq, Hash, Debug, Clone)] -enum RefLt { - Unnamed, - Static, - Named(LocalDefId), -} - fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, - decl: &'tcx FnDecl<'_>, + sig: &'tcx FnSig<'_>, body: Option, trait_sig: Option<&[Ident]>, generics: &'tcx Generics<'_>, @@ -164,7 +153,7 @@ fn check_fn_inner<'tcx>( for bound in pred.bounds { let mut visitor = RefVisitor::new(cx); walk_param_bound(&mut visitor, bound); - if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { + if visitor.lts.iter().any(|lt| matches!(lt.res, LifetimeName::Param(_))) { return; } if let GenericBound::Trait(ref trait_ref, _) = *bound { @@ -191,12 +180,12 @@ fn check_fn_inner<'tcx>( } } - if let Some(elidable_lts) = could_use_elision(cx, decl, body, trait_sig, generics.params) { + if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) { let lts = elidable_lts .iter() // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a // `Node::GenericParam`. - .filter_map(|&(def_id, _)| cx.tcx.hir().get_by_def_id(def_id).ident()) + .filter_map(|&def_id| cx.tcx.hir().get_by_def_id(def_id).ident()) .map(|ident| ident.to_string()) .collect::>() .join(", "); @@ -204,21 +193,99 @@ fn check_fn_inner<'tcx>( span_lint_and_then( cx, NEEDLESS_LIFETIMES, - span.with_hi(decl.output.span().hi()), + span.with_hi(sig.decl.output.span().hi()), &format!("the following explicit lifetimes could be elided: {lts}"), |diag| { - if let Some(span) = elidable_lts.iter().find_map(|&(_, span)| span) { - diag.span_help(span, "replace with `'_` in generic arguments such as here"); + if sig.header.is_async() { + // async functions have usages whose spans point at the lifetime declaration which messes up + // suggestions + return; + }; + + if let Some(suggestions) = elision_suggestions(cx, generics, &elidable_lts, &usages) { + diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); } }, ); } if report_extra_lifetimes { - self::report_extra_lifetimes(cx, decl, generics); + self::report_extra_lifetimes(cx, sig.decl, generics); } } +fn elision_suggestions( + cx: &LateContext<'_>, + generics: &Generics<'_>, + elidable_lts: &[LocalDefId], + usages: &[Lifetime], +) -> Option> { + let explicit_params = generics + .params + .iter() + .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) + .collect::>(); + + let mut suggestions = if elidable_lts.len() == explicit_params.len() { + // if all the params are elided remove the whole generic block + // + // fn x<'a>() {} + // ^^^^ + vec![(generics.span, String::new())] + } else { + elidable_lts + .iter() + .map(|&id| { + let pos = explicit_params.iter().position(|param| param.def_id == id)?; + let param = explicit_params.get(pos)?; + + let span = if let Some(next) = explicit_params.get(pos + 1) { + // fn x<'prev, 'a, 'next>() {} + // ^^^^ + param.span.until(next.span) + } else { + // `pos` should be at least 1 here, because the param in position 0 would either have a `next` + // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. + let prev = explicit_params.get(pos - 1)?; + + // fn x<'prev, 'a>() {} + // ^^^^ + param.span.with_lo(prev.span.hi()) + }; + + Some((span, String::new())) + }) + .collect::>>()? + }; + + suggestions.extend( + usages + .iter() + .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) + .map(|usage| { + match cx.tcx.hir().get_parent(usage.hir_id) { + Node::Ty(Ty { + kind: TyKind::Ref(..), .. + }) => { + // expand `&'a T` to `&'a T` + // ^^ ^^^ + let span = cx + .sess() + .source_map() + .span_extend_while(usage.ident.span, |ch| ch.is_ascii_whitespace()) + .unwrap_or(usage.ident.span); + + (span, String::new()) + }, + // `T<'a>` and `impl Foo + 'a` should be replaced by `'_` + _ => (usage.ident.span, String::from("'_")), + } + }), + ); + + Some(suggestions) +} + // elision doesn't work for explicit self types, see rust-lang/rust#69064 fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { if_chain! { @@ -238,13 +305,20 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: } } +fn named_lifetime(lt: &Lifetime) -> Option { + match lt.res { + LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), + _ => None, + } +} + fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, body: Option, trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], -) -> Option)>> { +) -> Option<(Vec, Vec)> { // There are two scenarios where elision works: // * no output references, all input references have different LT // * output references, exactly one input reference with same LT @@ -252,7 +326,7 @@ fn could_use_elision<'tcx>( // level of the current item. // check named LTs - let allowed_lts = allowed_lts_from(cx.tcx, named_generics); + let allowed_lts = allowed_lts_from(named_generics); // these will collect all the lifetimes for references in arg/return types let mut input_visitor = RefVisitor::new(cx); @@ -302,32 +376,24 @@ fn could_use_elision<'tcx>( // check for lifetimes from higher scopes for lt in input_lts.iter().chain(output_lts.iter()) { - if !allowed_lts.contains(lt) { + if let Some(id) = named_lifetime(lt) + && !allowed_lts.contains(&id) + { return None; } } // check for higher-ranked trait bounds if !input_visitor.nested_elision_site_lts.is_empty() || !output_visitor.nested_elision_site_lts.is_empty() { - let allowed_lts: FxHashSet<_> = allowed_lts - .iter() - .filter_map(|lt| match lt { - RefLt::Named(def_id) => Some(cx.tcx.item_name(def_id.to_def_id())), - _ => None, - }) - .collect(); + let allowed_lts: FxHashSet<_> = allowed_lts.iter().map(|id| cx.tcx.item_name(id.to_def_id())).collect(); for lt in input_visitor.nested_elision_site_lts { - if let RefLt::Named(def_id) = lt { - if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return None; - } + if allowed_lts.contains(<.ident.name) { + return None; } } for lt in output_visitor.nested_elision_site_lts { - if let RefLt::Named(def_id) = lt { - if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return None; - } + if allowed_lts.contains(<.ident.name) { + return None; } } } @@ -339,15 +405,10 @@ fn could_use_elision<'tcx>( let elidable_lts = named_lifetime_occurrences(&input_lts) .into_iter() .filter_map(|(def_id, occurrences)| { - if occurrences == 1 && (input_lts.len() == 1 || !output_lts.contains(&RefLt::Named(def_id))) { - Some(( - def_id, - input_visitor - .lifetime_generic_arg_spans - .get(&def_id) - .or_else(|| output_visitor.lifetime_generic_arg_spans.get(&def_id)) - .copied(), - )) + if occurrences == 1 + && (input_lts.len() == 1 || !output_lts.iter().any(|lt| named_lifetime(lt) == Some(def_id))) + { + Some(def_id) } else { None } @@ -355,31 +416,34 @@ fn could_use_elision<'tcx>( .collect::>(); if elidable_lts.is_empty() { - None - } else { - Some(elidable_lts) + return None; } + + let usages = itertools::chain(input_lts, output_lts).collect(); + + Some((elidable_lts, usages)) } -fn allowed_lts_from(tcx: TyCtxt<'_>, named_generics: &[GenericParam<'_>]) -> FxHashSet { - let mut allowed_lts = FxHashSet::default(); - for par in named_generics.iter() { - if let GenericParamKind::Lifetime { .. } = par.kind { - allowed_lts.insert(RefLt::Named(tcx.hir().local_def_id(par.hir_id))); - } - } - allowed_lts.insert(RefLt::Unnamed); - allowed_lts.insert(RefLt::Static); - allowed_lts +fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { + named_generics + .iter() + .filter_map(|par| { + if let GenericParamKind::Lifetime { .. } = par.kind { + Some(par.def_id) + } else { + None + } + }) + .collect() } /// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve /// relative order. #[must_use] -fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> { +fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> { let mut occurrences = Vec::new(); for lt in lts { - if let &RefLt::Named(curr_def_id) = lt { + if let Some(curr_def_id) = named_lifetime(lt) { if let Some(pair) = occurrences .iter_mut() .find(|(prev_def_id, _)| *prev_def_id == curr_def_id) @@ -393,12 +457,10 @@ fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> { occurrences } -/// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - lts: Vec, - lifetime_generic_arg_spans: FxHashMap, - nested_elision_site_lts: Vec, + lts: Vec, + nested_elision_site_lts: Vec, unelided_trait_object_lifetime: bool, } @@ -407,32 +469,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), - lifetime_generic_arg_spans: FxHashMap::default(), nested_elision_site_lts: Vec::new(), unelided_trait_object_lifetime: false, } } - fn record(&mut self, lifetime: &Option) { - if let Some(ref lt) = *lifetime { - if lt.is_static() { - self.lts.push(RefLt::Static); - } else if lt.is_anonymous() { - // Fresh lifetimes generated should be ignored. - self.lts.push(RefLt::Unnamed); - } else if let LifetimeName::Param(def_id) = lt.res { - self.lts.push(RefLt::Named(def_id)); - } - } else { - self.lts.push(RefLt::Unnamed); - } - } - - fn all_lts(&self) -> Vec { + fn all_lts(&self) -> Vec { self.lts .iter() .chain(self.nested_elision_site_lts.iter()) - .cloned() + .copied() .collect::>() } @@ -444,7 +490,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - self.record(&Some(*lifetime)); + self.lts.push(*lifetime); } fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) { @@ -469,11 +515,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { walk_item(self, item); self.lts.truncate(len); self.lts.extend(bounds.iter().filter_map(|bound| match bound { - GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id) = l.res { - RefLt::Named(def_id) - } else { - RefLt::Unnamed - }), + GenericArg::Lifetime(&l) => Some(l), _ => None, })); }, @@ -493,13 +535,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { _ => walk_ty(self, ty), } } - - fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { - if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res { - self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span); - } - walk_generic_arg(self, generic_arg); - } } /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to @@ -517,14 +552,18 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ return true; } // if the bounds define new lifetimes, they are fine to occur - let allowed_lts = allowed_lts_from(cx.tcx, pred.bound_generic_params); + let allowed_lts = allowed_lts_from(pred.bound_generic_params); // now walk the bounds for bound in pred.bounds.iter() { walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) { - return true; + for lt in visitor.all_lts() { + if let Some(id) = named_lifetime(<) + && !allowed_lts.contains(&id) + { + return true; + } } }, WherePredicate::EqPredicate(ref pred) => { diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr index 1f26c7f4db65..c5ea0b16d1be 100644 --- a/tests/ui/crashes/ice-2774.stderr +++ b/tests/ui/crashes/ice-2774.stderr @@ -5,6 +5,11 @@ LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` +help: elide the lifetimes + | +LL - pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { +LL + pub fn add_barfoos_to_foos(bars: &HashSet<&Bar>) { + | error: aborting due to previous error diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr index 875d5ab4f21c..0b0e0ad2684a 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -9,6 +9,11 @@ note: the lint level is defined here | LL | #![deny(clippy::needless_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: elide the lifetimes + | +LL - fn baz<'a>(&'a self) -> impl Foo + 'a { +LL + fn baz(&self) -> impl Foo + '_ { + | error: aborting due to previous error diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed new file mode 100644 index 000000000000..270cd1afc679 --- /dev/null +++ b/tests/ui/needless_lifetimes.fixed @@ -0,0 +1,537 @@ +// run-rustfix +// aux-build:macro_rules.rs + +#![warn(clippy::needless_lifetimes)] +#![allow( + unused, + clippy::boxed_local, + clippy::needless_pass_by_value, + clippy::unnecessary_wraps, + dyn_drop, + clippy::get_first +)] + +#[macro_use] +extern crate macro_rules; + +fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} + +fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} + +// No error; same lifetime on two params. +fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {} + +// No error; static involved. +fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {} + +fn mut_and_static_input(_x: &mut u8, _y: &'static str) {} + +fn in_and_out(x: &u8, _y: u8) -> &u8 { + x +} + +// No error; multiple input refs. +fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 +// ^^^ +fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 +// ^^^ +fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { + y +} + +// No error; multiple input refs +async fn func<'a>(args: &[&'a str]) -> Option<&'a str> { + args.get(0).cloned() +} + +// No error; static involved. +fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> +// ^^^ +fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { + Ok(x) +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> +// ^^^ +fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { + Ok(y) +} + +// No error; two input refs. +fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 { + x.unwrap() +} + +fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { + Ok(x) +} + +// Where-clause, but without lifetimes. +fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> +where + T: Copy, +{ + Ok(x) +} + +type Ref<'r> = &'r u8; + +// No error; same lifetime on two params. +fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {} + +fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} + +// No error; bounded lifetime. +fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {} + +// No error; bounded lifetime. +fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8) +where + 'b: 'a, +{ +} + +struct Lt<'a, I: 'static> { + x: &'a I, +} + +// No error; fn bound references `'a`. +fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +where + F: Fn(Lt<'a, I>) -> Lt<'a, I>, +{ + unreachable!() +} + +fn fn_bound_2(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> +where + for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>, +{ + unreachable!() +} + +// No error; see below. +fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) { + f(x); +} + +fn fn_bound_3_cannot_elide() { + let x = 42; + let p = &x; + let mut q = &x; + // This will fail if we elide lifetimes of `fn_bound_3`. + fn_bound_3(p, |y| q = y); +} + +// No error; multiple input refs. +fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () { + if cond { x } else { f() } +} + +struct X { + x: u8, +} + +impl X { + fn self_and_out(&self) -> &u8 { + &self.x + } + + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 + // ^^^ + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { + &self.x + } + + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 + // ^^^^^ + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { + x + } + + fn distinct_self_and_in(&self, _x: &u8) {} + + // No error; same lifetimes on two params. + fn self_and_same_in<'s>(&'s self, _x: &'s u8) {} +} + +struct Foo<'a>(&'a u8); + +impl<'a> Foo<'a> { + // No error; lifetime `'a` not defined in method. + fn self_shared_lifetime(&self, _: &'a u8) {} + // No error; bounds exist. + fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {} +} + +fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 { + unimplemented!() +} + +fn struct_with_lt(_foo: Foo<'_>) -> &str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `Foo`). +fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `Foo`). +fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str +// ^^ +fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str +// ^^^^ +fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { + unimplemented!() +} + +trait WithLifetime<'a> {} + +type WithLifetimeAlias<'a> = dyn WithLifetime<'a>; + +// Should not warn because it won't build without the lifetime. +fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str { + unimplemented!() +} + +// Should warn because there is no lifetime on `Drop`, so this would be +// unambiguous if we elided the lifetime. +fn trait_obj_elided2(_arg: &dyn Drop) -> &str { + unimplemented!() +} + +type FooAlias<'a> = Foo<'a>; + +fn alias_with_lt(_foo: FooAlias<'_>) -> &str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`). +fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`). +fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str +// ^^ +fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str +// ^^^^^^^^^ +fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { + unimplemented!() +} + +fn named_input_elided_output(_arg: &str) -> &str { + unimplemented!() +} + +fn elided_input_named_output<'a>(_arg: &str) -> &'a str { + unimplemented!() +} + +fn trait_bound_ok>(_: &u8, _: T) { + unimplemented!() +} +fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) { + unimplemented!() +} + +// Don't warn on these; see issue #292. +fn trait_bound_bug<'a, T: WithLifetime<'a>>() { + unimplemented!() +} + +// See issue #740. +struct Test { + vec: Vec, +} + +impl Test { + fn iter<'a>(&'a self) -> Box + 'a> { + unimplemented!() + } +} + +trait LintContext<'a> {} + +fn f<'a, T: LintContext<'a>>(_: &T) {} + +fn test<'a>(x: &'a [u8]) -> u8 { + let y: &'a u8 = &x[5]; + *y +} + +// Issue #3284: give hint regarding lifetime in return type. +struct Cow<'a> { + x: &'a str, +} +fn out_return_type_lts(e: &str) -> Cow<'_> { + unimplemented!() +} + +// Make sure we still warn on implementations +mod issue4291 { + trait BadTrait { + fn needless_lt(x: &u8) {} + } + + impl BadTrait for () { + fn needless_lt(_x: &u8) {} + } +} + +mod issue2944 { + trait Foo {} + struct Bar; + struct Baz<'a> { + bar: &'a Bar, + } + + impl<'a> Foo for Baz<'a> {} + impl Bar { + fn baz(&self) -> impl Foo + '_ { + Baz { bar: self } + } + } +} + +mod nested_elision_sites { + // issue #issue2944 + + // closure trait bounds subject to nested elision + // don't lint because they refer to outer lifetimes + fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + move || i + } + + // don't lint + fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 { + f() + } + fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + // lint + fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + f(i) + } + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 { + f(i) + } + + // don't lint + fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + // lint + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { + f(i) + } + + // don't lint + fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32 + where + T: Fn() -> &'a i32, + { + f() + } + // lint + fn where_clause_elidadable(i: &i32, f: T) -> &i32 + where + T: Fn(&i32) -> &i32, + { + f(i) + } + + // don't lint + fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 { + f(i) + } + fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + |i| i + } + // lint + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { + f(i) + } + + // don't lint + fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) { + |f| () + } + + // lint + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { + |f| () + } +} + +mod issue6159 { + use std::ops::Deref; + pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R + where + T: Deref, + F: FnOnce(&'a T::Target) -> R, + { + f(x.deref()) + } +} + +mod issue7296 { + use std::rc::Rc; + use std::sync::Arc; + + struct Foo; + impl Foo { + fn implicit(&self) -> &() { + &() + } + fn implicit_mut(&mut self) -> &() { + &() + } + + fn explicit<'a>(self: &'a Arc) -> &'a () { + &() + } + fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { + &() + } + + fn lifetime_elsewhere(self: Box, here: &()) -> &() { + &() + } + } + + trait Bar { + fn implicit(&self) -> &(); + fn implicit_provided(&self) -> &() { + &() + } + + fn explicit<'a>(self: &'a Arc) -> &'a (); + fn explicit_provided<'a>(self: &'a Arc) -> &'a () { + &() + } + + fn lifetime_elsewhere(self: Box, here: &()) -> &(); + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { + &() + } + } +} + +mod pr_9743_false_negative_fix { + #![allow(unused)] + + fn foo(x: &u8, y: &'_ u8) {} + + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} +} + +mod pr_9743_output_lifetime_checks { + #![allow(unused)] + + // lint: only one input + fn one_input(x: &u8) -> &u8 { + unimplemented!() + } + + // lint: multiple inputs, output would not be elided + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 { + unimplemented!() + } + + // don't lint: multiple inputs, output would be elided (which would create an ambiguity) + fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'a u8 { + unimplemented!() + } +} + +mod in_macro { + macro_rules! local_one_input_macro { + () => { + fn one_input(x: &u8) -> &u8 { + unimplemented!() + } + }; + } + + // lint local macro expands to function with needless lifetimes + local_one_input_macro!(); + + // no lint on external macro + macro_rules::needless_lifetime!(); +} + +mod issue5787 { + use std::sync::MutexGuard; + + struct Foo; + + impl Foo { + // doesn't get linted without async + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + guard + } + } + + async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str { + y + } +} + +fn main() {} diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 78493c6d0672..5d4dc971b8d2 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,7 +1,9 @@ +// run-rustfix // aux-build:macro_rules.rs + #![warn(clippy::needless_lifetimes)] #![allow( - dead_code, + unused, clippy::boxed_local, clippy::needless_pass_by_value, clippy::unnecessary_wraps, @@ -515,4 +517,21 @@ mod in_macro { macro_rules::needless_lifetime!(); } +mod issue5787 { + use std::sync::MutexGuard; + + struct Foo; + + impl Foo { + // doesn't get linted without async + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + guard + } + } + + async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str { + y + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index 9d02626956e0..afe637ac3888 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,319 +1,546 @@ error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:15:1 + --> $DIR/needless_lifetimes.rs:17:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` +help: elide the lifetimes + | +LL - fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} +LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} + | error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:17:1 + --> $DIR/needless_lifetimes.rs:19:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} +LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:27:1 + --> $DIR/needless_lifetimes.rs:29:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { +LL + fn in_and_out(x: &u8, _y: u8) -> &u8 { + | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:39:1 + --> $DIR/needless_lifetimes.rs:41:1 | LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { +LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:46:1 + --> $DIR/needless_lifetimes.rs:48:1 | LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { +LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { + | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:63:1 + --> $DIR/needless_lifetimes.rs:65:1 | LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { +LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:70:1 + --> $DIR/needless_lifetimes.rs:72:1 | LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { +LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:79:1 + --> $DIR/needless_lifetimes.rs:81:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { +LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:84:1 + --> $DIR/needless_lifetimes.rs:86:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> +LL + fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> + | error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:96:1 + --> $DIR/needless_lifetimes.rs:98:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:96:37 +help: elide the lifetimes + | +LL - fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} +LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} | -LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:120:1 + --> $DIR/needless_lifetimes.rs:122:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:120:32 +help: elide the lifetimes + | +LL - fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +LL + fn fn_bound_2(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> | -LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> - | ^^ error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:150:5 + --> $DIR/needless_lifetimes.rs:152:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_out<'s>(&'s self) -> &'s u8 { +LL + fn self_and_out(&self) -> &u8 { + | error: the following explicit lifetimes could be elided: 't - --> $DIR/needless_lifetimes.rs:157:5 + --> $DIR/needless_lifetimes.rs:159:5 | LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { +LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { + | error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:164:5 + --> $DIR/needless_lifetimes.rs:166:5 | LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { +LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { + | error: the following explicit lifetimes could be elided: 's, 't - --> $DIR/needless_lifetimes.rs:168:5 + --> $DIR/needless_lifetimes.rs:170:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} +LL + fn distinct_self_and_in(&self, _x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:187:1 + --> $DIR/needless_lifetimes.rs:189:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:187:33 +help: elide the lifetimes + | +LL - fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { +LL + fn struct_with_lt(_foo: Foo<'_>) -> &str { | -LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:205:1 + --> $DIR/needless_lifetimes.rs:207:1 | LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:205:43 +help: elide the lifetimes + | +LL - fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { +LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { | -LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:213:1 + --> $DIR/needless_lifetimes.rs:215:1 | LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { +LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:228:1 + --> $DIR/needless_lifetimes.rs:230:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { +LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:234:1 + --> $DIR/needless_lifetimes.rs:236:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:234:37 +help: elide the lifetimes + | +LL - fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { +LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str { | -LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:252:1 + --> $DIR/needless_lifetimes.rs:254:1 | LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:252:47 +help: elide the lifetimes + | +LL - fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { +LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { | -LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:260:1 + --> $DIR/needless_lifetimes.rs:262:1 | LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { +LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:264:1 + --> $DIR/needless_lifetimes.rs:266:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn named_input_elided_output<'a>(_arg: &'a str) -> &str { +LL + fn named_input_elided_output(_arg: &str) -> &str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:272:1 + --> $DIR/needless_lifetimes.rs:274:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { +LL + fn trait_bound_ok>(_: &u8, _: T) { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:308:1 + --> $DIR/needless_lifetimes.rs:310:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:308:47 +help: elide the lifetimes + | +LL - fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { +LL + fn out_return_type_lts(e: &str) -> Cow<'_> { | -LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:315:9 + --> $DIR/needless_lifetimes.rs:317:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn needless_lt<'a>(x: &'a u8) {} +LL + fn needless_lt(x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:319:9 + --> $DIR/needless_lifetimes.rs:321:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn needless_lt<'a>(_x: &'a u8) {} +LL + fn needless_lt(_x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:332:9 + --> $DIR/needless_lifetimes.rs:334:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn baz<'a>(&'a self) -> impl Foo + 'a { +LL + fn baz(&self) -> impl Foo + '_ { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:364:5 + --> $DIR/needless_lifetimes.rs:366:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { +LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:373:5 + --> $DIR/needless_lifetimes.rs:375:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { +LL + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:385:5 + --> $DIR/needless_lifetimes.rs:387:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 +LL + fn where_clause_elidadable(i: &i32, f: T) -> &i32 + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:400:5 + --> $DIR/needless_lifetimes.rs:402:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { +LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:413:5 + --> $DIR/needless_lifetimes.rs:415:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { +LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:416:5 + --> $DIR/needless_lifetimes.rs:418:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { +LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:438:9 + --> $DIR/needless_lifetimes.rs:440:9 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit<'a>(&'a self) -> &'a () { +LL + fn implicit(&self) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:441:9 + --> $DIR/needless_lifetimes.rs:443:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit_mut<'a>(&'a mut self) -> &'a () { +LL + fn implicit_mut(&mut self) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:452:9 + --> $DIR/needless_lifetimes.rs:454:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { +LL + fn lifetime_elsewhere(self: Box, here: &()) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:458:9 + --> $DIR/needless_lifetimes.rs:460:9 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit<'a>(&'a self) -> &'a (); +LL + fn implicit(&self) -> &(); + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:459:9 + --> $DIR/needless_lifetimes.rs:461:9 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit_provided<'a>(&'a self) -> &'a () { +LL + fn implicit_provided(&self) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:468:9 + --> $DIR/needless_lifetimes.rs:470:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); +LL + fn lifetime_elsewhere(self: Box, here: &()) -> &(); + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:469:9 + --> $DIR/needless_lifetimes.rs:471:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { +LL + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:478:5 + --> $DIR/needless_lifetimes.rs:480:5 | LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn foo<'a>(x: &'a u8, y: &'_ u8) {} +LL + fn foo(x: &u8, y: &'_ u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:480:5 + --> $DIR/needless_lifetimes.rs:482:5 | LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} +LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:487:5 + --> $DIR/needless_lifetimes.rs:489:5 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:492:5 + --> $DIR/needless_lifetimes.rs:494:5 | LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { +LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:505:13 + --> $DIR/needless_lifetimes.rs:507:13 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -322,6 +549,11 @@ LL | local_one_input_macro!(); | ------------------------ in this macro invocation | = note: this error originates in the macro `local_one_input_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: elide the lifetimes + | +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { + | error: aborting due to 46 previous errors From ef2596155d8659b9546af9af6360e0ffd6ffa5a2 Mon Sep 17 00:00:00 2001 From: KaDiWa Date: Tue, 31 Jan 2023 23:23:37 +0100 Subject: [PATCH 521/524] update some dependencies --- Cargo.toml | 2 +- clippy_dev/Cargo.toml | 2 +- clippy_dev/src/main.rs | 99 +++++++++++++++++++++++------------------ clippy_lints/Cargo.toml | 2 +- lintcheck/Cargo.toml | 4 +- 5 files changed, 60 insertions(+), 49 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dc94b1045249..70d1268090f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ filetime = "0.2" rustc-workspace-hack = "1.0" # UI test dependencies -clap = { version = "3.1", features = ["derive"] } +clap = { version = "4.1.4", features = ["derive"] } clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 510c7e852af6..c3f8a782d273 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] aho-corasick = "0.7" -clap = "3.2" +clap = "4.1.4" indoc = "1.0" itertools = "0.10.1" opener = "0.5" diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index d3e036692040..b2d67a72fd2b 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -2,7 +2,7 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue}; +use clap::{Arg, ArgAction, ArgMatches, Command}; use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; @@ -110,24 +110,37 @@ fn get_clap_config() -> ArgMatches { Command::new("bless").about("bless the test output changes").arg( Arg::new("ignore-timestamp") .long("ignore-timestamp") + .action(ArgAction::SetTrue) .help("Include files updated before clippy was built"), ), Command::new("dogfood").about("Runs the dogfood test").args([ - Arg::new("fix").long("fix").help("Apply the suggestions when possible"), + Arg::new("fix") + .long("fix") + .action(ArgAction::SetTrue) + .help("Apply the suggestions when possible"), Arg::new("allow-dirty") .long("allow-dirty") + .action(ArgAction::SetTrue) .help("Fix code even if the working directory has changes") .requires("fix"), Arg::new("allow-staged") .long("allow-staged") + .action(ArgAction::SetTrue) .help("Fix code even if the working directory has staged changes") .requires("fix"), ]), Command::new("fmt") .about("Run rustfmt on all projects and tests") .args([ - Arg::new("check").long("check").help("Use the rustfmt --check option"), - Arg::new("verbose").short('v').long("verbose").help("Echo commands run"), + Arg::new("check") + .long("check") + .action(ArgAction::SetTrue) + .help("Use the rustfmt --check option"), + Arg::new("verbose") + .short('v') + .long("verbose") + .action(ArgAction::SetTrue) + .help("Echo commands run"), ]), Command::new("update_lints") .about("Updates lint registration and information from the source code") @@ -140,13 +153,17 @@ fn get_clap_config() -> ArgMatches { * all lints are registered in the lint store", ) .args([ - Arg::new("print-only").long("print-only").help( - "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", - ), + Arg::new("print-only") + .long("print-only") + .action(ArgAction::SetTrue) + .help( + "Print a table of lints to STDOUT. \ + This does not include deprecated and internal lints. \ + (Does not modify any files)", + ), Arg::new("check") .long("check") + .action(ArgAction::SetTrue) .help("Checks that `cargo dev update_lints` has been run. Used on CI."), ]), Command::new("new_lint") @@ -156,15 +173,13 @@ fn get_clap_config() -> ArgMatches { .short('p') .long("pass") .help("Specify whether the lint runs during the early or late pass") - .takes_value(true) - .value_parser([PossibleValue::new("early"), PossibleValue::new("late")]) + .value_parser(["early", "late"]) .conflicts_with("type") .required_unless_present("type"), Arg::new("name") .short('n') .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") - .takes_value(true) .required(true), Arg::new("category") .short('c') @@ -172,25 +187,23 @@ fn get_clap_config() -> ArgMatches { .help("What category the lint belongs to") .default_value("nursery") .value_parser([ - PossibleValue::new("style"), - PossibleValue::new("correctness"), - PossibleValue::new("suspicious"), - PossibleValue::new("complexity"), - PossibleValue::new("perf"), - PossibleValue::new("pedantic"), - PossibleValue::new("restriction"), - PossibleValue::new("cargo"), - PossibleValue::new("nursery"), - PossibleValue::new("internal"), - PossibleValue::new("internal_warn"), - ]) - .takes_value(true), - Arg::new("type") - .long("type") - .help("What directory the lint belongs in") - .takes_value(true) - .required(false), - Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"), + "style", + "correctness", + "suspicious", + "complexity", + "perf", + "pedantic", + "restriction", + "cargo", + "nursery", + "internal", + "internal_warn", + ]), + Arg::new("type").long("type").help("What directory the lint belongs in"), + Arg::new("msrv") + .long("msrv") + .action(ArgAction::SetTrue) + .help("Add MSRV config code to the lint"), ]), Command::new("setup") .about("Support for setting up your personal development environment") @@ -201,13 +214,12 @@ fn get_clap_config() -> ArgMatches { .args([ Arg::new("remove") .long("remove") - .help("Remove the dependencies added with 'cargo dev setup intellij'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the dependencies added with 'cargo dev setup intellij'"), Arg::new("rustc-repo-path") .long("repo-path") .short('r') .help("The path to a rustc repo that will be used for setting the dependencies") - .takes_value(true) .value_name("path") .conflicts_with("remove") .required(true), @@ -217,26 +229,26 @@ fn get_clap_config() -> ArgMatches { .args([ Arg::new("remove") .long("remove") - .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"), Arg::new("force-override") .long("force-override") .short('f') - .help("Forces the override of an existing git pre-commit hook") - .required(false), + .action(ArgAction::SetTrue) + .help("Forces the override of an existing git pre-commit hook"), ]), Command::new("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") .args([ Arg::new("remove") .long("remove") - .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the tasks added with 'cargo dev setup vscode-tasks'"), Arg::new("force-override") .long("force-override") .short('f') - .help("Forces the override of existing vscode tasks") - .required(false), + .action(ArgAction::SetTrue) + .help("Forces the override of existing vscode tasks"), ]), ]), Command::new("remove") @@ -295,6 +307,7 @@ fn get_clap_config() -> ArgMatches { .help("The new name of the lint"), Arg::new("uplift") .long("uplift") + .action(ArgAction::SetTrue) .help("This lint will be uplifted into rustc"), ]), Command::new("deprecate").about("Deprecates the given lint").args([ @@ -305,8 +318,6 @@ fn get_clap_config() -> ArgMatches { Arg::new("reason") .long("reason") .short('r') - .required(false) - .takes_value(true) .help("The reason for deprecation"), ]), ]) diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 7278ad13d568..989e4d3fa56c 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2021" [dependencies] -cargo_metadata = "0.14" +cargo_metadata = "0.15.3" clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } if_chain = "1.0" diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index de31c16b819e..653121af54dc 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -10,8 +10,8 @@ edition = "2021" publish = false [dependencies] -cargo_metadata = "0.14" -clap = "3.2" +cargo_metadata = "0.15.3" +clap = "4.1.4" crossbeam-channel = "0.5.6" flate2 = "1.0" rayon = "1.5.1" From f7d59b2e574888912cf7fb670c742b4636e451d0 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 1 Feb 2023 22:50:43 +0100 Subject: [PATCH 522/524] Don't depend on FormatArgsExpn in ManualAssert. --- clippy_lints/src/manual_assert.rs | 97 +++++++++++++++---------------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 4277455a3a21..ce5d657bcf0e 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,7 +1,6 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::macros::root_macro_call; use clippy_utils::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; @@ -38,57 +37,57 @@ declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); impl<'tcx> LateLintPass<'tcx> for ManualAssert { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if_chain! { - if let ExprKind::If(cond, then, None) = expr.kind; - if !matches!(cond.kind, ExprKind::Let(_)); - if !expr.span.from_expansion(); - let then = peel_blocks_with_stmt(then); - if let Some(macro_call) = root_macro_call(then.span); - if cx.tcx.item_name(macro_call.def_id) == sym::panic; - if !cx.tcx.sess.source_map().is_multiline(cond.span); - if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); + if let ExprKind::If(cond, then, None) = expr.kind + && !matches!(cond.kind, ExprKind::Let(_)) + && !expr.span.from_expansion() + && let then = peel_blocks_with_stmt(then) + && let Some(macro_call) = root_macro_call(then.span) + && cx.tcx.item_name(macro_call.def_id) == sym::panic + && !cx.tcx.sess.source_map().is_multiline(cond.span) + && let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span) + && let Some(panic_snippet) = panic_snippet.strip_suffix(')') + && let Some((_, format_args_snip)) = panic_snippet.split_once('(') // Don't change `else if foo { panic!(..) }` to `else { assert!(foo, ..) }` as it just // shuffles the condition around. // Should this have a config value? - if !is_else_clause(cx.tcx, expr); - then { - let mut applicability = Applicability::MachineApplicable; - let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); - let cond = cond.peel_drop_temps(); - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); - if !comments.is_empty() { - comments += "\n"; - } - let (cond, not) = match cond.kind { - ExprKind::Unary(UnOp::Not, e) => (e, ""), - _ => (cond, "!"), - }; - let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); - let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); - // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block - span_lint_and_then( - cx, - MANUAL_ASSERT, - expr.span, - "only a `panic!` in `if`-then statement", - |diag| { - // comments can be noisy, do not show them to the user - if !comments.is_empty() { - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability); - } - diag.span_suggestion( - expr.span, - "try instead", - sugg, - applicability); - } - - ); + && !is_else_clause(cx.tcx, expr) + { + let mut applicability = Applicability::MachineApplicable; + let cond = cond.peel_drop_temps(); + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); + if !comments.is_empty() { + comments += "\n"; } + let (cond, not) = match cond.kind { + ExprKind::Unary(UnOp::Not, e) => (e, ""), + _ => (cond, "!"), + }; + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); + // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block + span_lint_and_then( + cx, + MANUAL_ASSERT, + expr.span, + "only a `panic!` in `if`-then statement", + |diag| { + // comments can be noisy, do not show them to the user + if !comments.is_empty() { + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability + ); + } + diag.span_suggestion( + expr.span, + "try instead", + sugg, + applicability + ); + } + ); } } } From ecd98bad45841d30ff83269b94da7a8c06da0516 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 1 Feb 2023 22:51:02 +0100 Subject: [PATCH 523/524] Bless tests. --- tests/ui/manual_assert.edition2018.fixed | 35 ++++-------- tests/ui/manual_assert.edition2018.stderr | 67 ++++++++++++++++++++++- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index 638320dd6eec..8c7e919bf62a 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -29,9 +29,7 @@ fn main() { panic!("qaqaq{:?}", a); } assert!(a.is_empty(), "qaqaq{:?}", a); - if !a.is_empty() { - panic!("qwqwq"); - } + assert!(a.is_empty(), "qwqwq"); if a.len() == 3 { println!("qwq"); println!("qwq"); @@ -46,21 +44,11 @@ fn main() { println!("qwq"); } let b = vec![1, 2, 3]; - if b.is_empty() { - panic!("panic1"); - } - if b.is_empty() && a.is_empty() { - panic!("panic2"); - } - if a.is_empty() && !b.is_empty() { - panic!("panic3"); - } - if b.is_empty() || a.is_empty() { - panic!("panic4"); - } - if a.is_empty() || !b.is_empty() { - panic!("panic5"); - } + assert!(!b.is_empty(), "panic1"); + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); if a.is_empty() { let _ = 0; @@ -71,12 +59,11 @@ fn main() { fn issue7730(a: u8) { // Suggestion should preserve comment - if a > 2 { - // comment - /* this is a + // comment +/* this is a multiline comment */ - /// Doc comment - panic!("panic with comment") // comment after `panic!` - } +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); } diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index 1f2e1e3087bd..3555ac29243a 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -8,6 +8,54 @@ LL | | } | = note: `-D clippy::manual-assert` implied by `-D warnings` +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:34:5 + | +LL | / if !a.is_empty() { +LL | | panic!("qwqwq"); +LL | | } + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:51:5 + | +LL | / if b.is_empty() { +LL | | panic!("panic1"); +LL | | } + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:54:5 + | +LL | / if b.is_empty() && a.is_empty() { +LL | | panic!("panic2"); +LL | | } + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:57:5 + | +LL | / if a.is_empty() && !b.is_empty() { +LL | | panic!("panic3"); +LL | | } + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:60:5 + | +LL | / if b.is_empty() || a.is_empty() { +LL | | panic!("panic4"); +LL | | } + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:63:5 + | +LL | / if a.is_empty() || !b.is_empty() { +LL | | panic!("panic5"); +LL | | } + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 | @@ -16,5 +64,22 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` -error: aborting due to 2 previous errors +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:78:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` +LL | | } + | |_____^ + | +help: try instead + | +LL | assert!(!(a > 2), "panic with comment"); + | + +error: aborting due to 9 previous errors From fba16e2e3a6f743cb23c7793851a4f0201b21722 Mon Sep 17 00:00:00 2001 From: Michael Krasnitski Date: Thu, 2 Feb 2023 19:36:42 -0500 Subject: [PATCH 524/524] Add `extra_unused_type_parameters` lint --- CHANGELOG.md | 1 + README.md | 2 +- book/src/README.md | 2 +- clippy_lints/src/declared_lints.rs | 1 + .../src/extra_unused_type_parameters.rs | 178 ++++++++++++++++++ clippy_lints/src/lib.rs | 2 + tests/ui/extra_unused_type_parameters.rs | 69 +++++++ tests/ui/extra_unused_type_parameters.stderr | 59 ++++++ tests/ui/needless_lifetimes.fixed | 1 + tests/ui/needless_lifetimes.rs | 1 + tests/ui/needless_lifetimes.stderr | 92 ++++----- tests/ui/new_without_default.rs | 7 +- tests/ui/new_without_default.stderr | 14 +- tests/ui/redundant_field_names.fixed | 2 +- tests/ui/redundant_field_names.rs | 2 +- .../ui/seek_to_start_instead_of_rewind.fixed | 4 +- tests/ui/seek_to_start_instead_of_rewind.rs | 4 +- tests/ui/type_repetition_in_bounds.rs | 1 + tests/ui/type_repetition_in_bounds.stderr | 8 +- 19 files changed, 384 insertions(+), 66 deletions(-) create mode 100644 clippy_lints/src/extra_unused_type_parameters.rs create mode 100644 tests/ui/extra_unused_type_parameters.rs create mode 100644 tests/ui/extra_unused_type_parameters.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index e2cde09776f4..659e8aebcd57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4383,6 +4383,7 @@ Released 2018-09-13 [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice [`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain [`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes +[`extra_unused_type_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters [`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from [`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default [`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file diff --git a/README.md b/README.md index ab44db694835..95f6d2cc45c8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/book/src/README.md b/book/src/README.md index 23867df8efe1..df4a1f2702e4 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 36a366fc9747..457a25826e79 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -156,6 +156,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO, crate::exit::EXIT_INFO, crate::explicit_write::EXPLICIT_WRITE_INFO, + crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO, crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO, crate::float_literal::EXCESSIVE_PRECISION_INFO, crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs new file mode 100644 index 000000000000..2fdd8a71466c --- /dev/null +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -0,0 +1,178 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::trait_ref_of_method; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::MultiSpan; +use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor}; +use rustc_hir::{ + GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, PredicateOrigin, Ty, TyKind, WherePredicate, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{def_id::DefId, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for type parameters in generics that are never used anywhere else. + /// + /// ### Why is this bad? + /// Functions cannot infer the value of unused type parameters; therefore, calling them + /// requires using a turbofish, which serves no purpose but to satisfy the compiler. + /// + /// ### Example + /// ```rust + /// // unused type parameters + /// fn unused_ty(x: u8) { + /// // .. + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn no_unused_ty(x: u8) { + /// // .. + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub EXTRA_UNUSED_TYPE_PARAMETERS, + complexity, + "unused type parameters in function definitions" +} +declare_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]); + +/// A visitor struct that walks a given function and gathers generic type parameters, plus any +/// trait bounds those parameters have. +struct TypeWalker<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + /// Collection of all the type parameters and their spans. + ty_params: FxHashMap, + /// Collection of any (inline) trait bounds corresponding to each type parameter. + bounds: FxHashMap, + /// The entire `Generics` object of the function, useful for querying purposes. + generics: &'tcx Generics<'tcx>, + /// The value of this will remain `true` if *every* parameter: + /// 1. Is a type parameter, and + /// 2. Goes unused in the function. + /// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic + /// parameters are present, this will be set to `false`. + all_params_unused: bool, +} + +impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { + fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> Self { + let mut all_params_unused = true; + let ty_params = generics + .params + .iter() + .filter_map(|param| { + if let GenericParamKind::Type { .. } = param.kind { + Some((param.def_id.into(), param.span)) + } else { + if !param.is_elided_lifetime() { + all_params_unused = false; + } + None + } + }) + .collect(); + Self { + cx, + ty_params, + bounds: FxHashMap::default(), + generics, + all_params_unused, + } + } + + fn emit_lint(&self) { + let (msg, help) = match self.ty_params.len() { + 0 => return, + 1 => ( + "type parameter goes unused in function definition", + "consider removing the parameter", + ), + _ => ( + "type parameters go unused in function definition", + "consider removing the parameters", + ), + }; + + let source_map = self.cx.tcx.sess.source_map(); + let span = if self.all_params_unused { + self.generics.span.into() // Remove the entire list of generics + } else { + MultiSpan::from_spans( + self.ty_params + .iter() + .map(|(def_id, &span)| { + // Extend the span past any trait bounds, and include the comma at the end. + let span_to_extend = self.bounds.get(def_id).copied().map_or(span, Span::shrink_to_hi); + let comma_range = source_map.span_extend_to_next_char(span_to_extend, '>', false); + let comma_span = source_map.span_through_char(comma_range, ','); + span.with_hi(comma_span.hi()) + }) + .collect(), + ) + }; + + span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, span, msg, None, help); + } +} + +impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { + if let Some((def_id, _)) = t.peel_refs().as_generic_param() { + if self.ty_params.remove(&def_id).is_some() { + self.all_params_unused = false; + } + } else if let TyKind::OpaqueDef(id, _, _) = t.kind { + // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls + // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're + // using `OnlyBodies`, so the check ends up failing and the type isn't fully walked. + let item = self.nested_visit_map().item(id); + walk_item(self, item); + } else { + walk_ty(self, t); + } + } + + fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) { + if let WherePredicate::BoundPredicate(predicate) = predicate { + // Collect spans for bounds that appear in the list of generics (not in a where-clause) + // for use in forming the help message + if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() + && let PredicateOrigin::GenericParam = predicate.origin + { + self.bounds.insert(def_id, predicate.span); + } + // Only walk the right-hand side of where-bounds + for bound in predicate.bounds { + walk_param_bound(self, bound); + } + } + } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } +} + +impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn(_, generics, _) = item.kind { + let mut walker = TypeWalker::new(cx, generics); + walk_item(&mut walker, item); + walker.emit_lint(); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { + // Only lint on inherent methods, not trait methods. + if let ImplItemKind::Fn(..) = item.kind && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { + let mut walker = TypeWalker::new(cx, item.generics); + walk_impl_item(&mut walker, item); + walker.emit_lint(); + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5c4b60410441..2f5c4adca9f1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -122,6 +122,7 @@ mod excessive_bools; mod exhaustive_items; mod exit; mod explicit_write; +mod extra_unused_type_parameters; mod fallible_impl_from; mod float_literal; mod floating_point_arithmetic; @@ -910,6 +911,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)); + store.register_late_pass(|_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/extra_unused_type_parameters.rs b/tests/ui/extra_unused_type_parameters.rs new file mode 100644 index 000000000000..5cb80cb6233f --- /dev/null +++ b/tests/ui/extra_unused_type_parameters.rs @@ -0,0 +1,69 @@ +#![allow(unused, clippy::needless_lifetimes)] +#![warn(clippy::extra_unused_type_parameters)] + +fn unused_ty(x: u8) {} + +fn unused_multi(x: u8) {} + +fn unused_with_lt<'a, T>(x: &'a u8) {} + +fn used_ty(x: T, y: u8) {} + +fn used_ref<'a, T>(x: &'a T) {} + +fn used_ret(x: u8) -> T { + T::default() +} + +fn unused_bounded(x: U) {} + +fn unused_where_clause(x: U) +where + T: Default, +{ +} + +fn some_unused, E>(b: B, c: C) {} + +fn used_opaque
    (iter: impl Iterator) -> usize { + iter.count() +} + +fn used_ret_opaque() -> impl Iterator { + std::iter::empty() +} + +fn used_vec_box(x: Vec>) {} + +fn used_body() -> String { + T::default().to_string() +} + +fn used_closure() -> impl Fn() { + || println!("{}", T::default().to_string()) +} + +struct S; + +impl S { + fn unused_ty_impl(&self) {} +} + +// Don't lint on trait methods +trait Foo { + fn bar(&self); +} + +impl Foo for S { + fn bar(&self) {} +} + +fn skip_index(iter: Iter, index: usize) -> impl Iterator +where + Iter: Iterator, +{ + iter.enumerate() + .filter_map(move |(i, a)| if i == index { None } else { Some(a) }) +} + +fn main() {} diff --git a/tests/ui/extra_unused_type_parameters.stderr b/tests/ui/extra_unused_type_parameters.stderr new file mode 100644 index 000000000000..1c8dd53e6385 --- /dev/null +++ b/tests/ui/extra_unused_type_parameters.stderr @@ -0,0 +1,59 @@ +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:4:13 + | +LL | fn unused_ty(x: u8) {} + | ^^^ + | + = help: consider removing the parameter + = note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings` + +error: type parameters go unused in function definition + --> $DIR/extra_unused_type_parameters.rs:6:16 + | +LL | fn unused_multi(x: u8) {} + | ^^^^^^ + | + = help: consider removing the parameters + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:8:23 + | +LL | fn unused_with_lt<'a, T>(x: &'a u8) {} + | ^ + | + = help: consider removing the parameter + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:18:19 + | +LL | fn unused_bounded(x: U) {} + | ^^^^^^^^^^^ + | + = help: consider removing the parameter + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:20:24 + | +LL | fn unused_where_clause(x: U) + | ^^ + | + = help: consider removing the parameter + +error: type parameters go unused in function definition + --> $DIR/extra_unused_type_parameters.rs:26:16 + | +LL | fn some_unused, E>(b: B, c: C) {} + | ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ + | + = help: consider removing the parameters + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:49:22 + | +LL | fn unused_ty_impl(&self) {} + | ^^^ + | + = help: consider removing the parameter + +error: aborting due to 7 previous errors + diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed index 270cd1afc679..d286ef4ba378 100644 --- a/tests/ui/needless_lifetimes.fixed +++ b/tests/ui/needless_lifetimes.fixed @@ -5,6 +5,7 @@ #![allow( unused, clippy::boxed_local, + clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, clippy::unnecessary_wraps, dyn_drop, diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 5d4dc971b8d2..409528b291db 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -5,6 +5,7 @@ #![allow( unused, clippy::boxed_local, + clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, clippy::unnecessary_wraps, dyn_drop, diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index afe637ac3888..4e3c8f20d8c5 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,5 +1,5 @@ error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:17:1 + --> $DIR/needless_lifetimes.rs:18:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} | error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:19:1 + --> $DIR/needless_lifetimes.rs:20:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:29:1 + --> $DIR/needless_lifetimes.rs:30:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL + fn in_and_out(x: &u8, _y: u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:41:1 + --> $DIR/needless_lifetimes.rs:42:1 | LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:48:1 + --> $DIR/needless_lifetimes.rs:49:1 | LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:65:1 + --> $DIR/needless_lifetimes.rs:66:1 | LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:72:1 + --> $DIR/needless_lifetimes.rs:73:1 | LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:81:1 + --> $DIR/needless_lifetimes.rs:82:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:86:1 + --> $DIR/needless_lifetimes.rs:87:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL + fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> | error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:98:1 + --> $DIR/needless_lifetimes.rs:99:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:122:1 + --> $DIR/needless_lifetimes.rs:123:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL + fn fn_bound_2(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> | error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:152:5 + --> $DIR/needless_lifetimes.rs:153:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL + fn self_and_out(&self) -> &u8 { | error: the following explicit lifetimes could be elided: 't - --> $DIR/needless_lifetimes.rs:159:5 + --> $DIR/needless_lifetimes.rs:160:5 | LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { | error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:166:5 + --> $DIR/needless_lifetimes.rs:167:5 | LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { | error: the following explicit lifetimes could be elided: 's, 't - --> $DIR/needless_lifetimes.rs:170:5 + --> $DIR/needless_lifetimes.rs:171:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -180,7 +180,7 @@ LL + fn distinct_self_and_in(&self, _x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:189:1 + --> $DIR/needless_lifetimes.rs:190:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -192,7 +192,7 @@ LL + fn struct_with_lt(_foo: Foo<'_>) -> &str { | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:207:1 + --> $DIR/needless_lifetimes.rs:208:1 | LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -204,7 +204,7 @@ LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:215:1 + --> $DIR/needless_lifetimes.rs:216:1 | LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -216,7 +216,7 @@ LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:230:1 + --> $DIR/needless_lifetimes.rs:231:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -228,7 +228,7 @@ LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:236:1 + --> $DIR/needless_lifetimes.rs:237:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -240,7 +240,7 @@ LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str { | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:254:1 + --> $DIR/needless_lifetimes.rs:255:1 | LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -252,7 +252,7 @@ LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:262:1 + --> $DIR/needless_lifetimes.rs:263:1 | LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -264,7 +264,7 @@ LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:266:1 + --> $DIR/needless_lifetimes.rs:267:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -276,7 +276,7 @@ LL + fn named_input_elided_output(_arg: &str) -> &str { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:274:1 + --> $DIR/needless_lifetimes.rs:275:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -288,7 +288,7 @@ LL + fn trait_bound_ok>(_: &u8, _: T) { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:310:1 + --> $DIR/needless_lifetimes.rs:311:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -300,7 +300,7 @@ LL + fn out_return_type_lts(e: &str) -> Cow<'_> { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:317:9 + --> $DIR/needless_lifetimes.rs:318:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -312,7 +312,7 @@ LL + fn needless_lt(x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:321:9 + --> $DIR/needless_lifetimes.rs:322:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -324,7 +324,7 @@ LL + fn needless_lt(_x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:334:9 + --> $DIR/needless_lifetimes.rs:335:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -336,7 +336,7 @@ LL + fn baz(&self) -> impl Foo + '_ { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:366:5 + --> $DIR/needless_lifetimes.rs:367:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -348,7 +348,7 @@ LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(& | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:375:5 + --> $DIR/needless_lifetimes.rs:376:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -360,7 +360,7 @@ LL + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:387:5 + --> $DIR/needless_lifetimes.rs:388:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -372,7 +372,7 @@ LL + fn where_clause_elidadable(i: &i32, f: T) -> &i32 | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:402:5 + --> $DIR/needless_lifetimes.rs:403:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -384,7 +384,7 @@ LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:415:5 + --> $DIR/needless_lifetimes.rs:416:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -396,7 +396,7 @@ LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:418:5 + --> $DIR/needless_lifetimes.rs:419:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -408,7 +408,7 @@ LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:440:9 + --> $DIR/needless_lifetimes.rs:441:9 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -420,7 +420,7 @@ LL + fn implicit(&self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:443:9 + --> $DIR/needless_lifetimes.rs:444:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -432,7 +432,7 @@ LL + fn implicit_mut(&mut self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:454:9 + --> $DIR/needless_lifetimes.rs:455:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -444,7 +444,7 @@ LL + fn lifetime_elsewhere(self: Box, here: &()) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:460:9 + --> $DIR/needless_lifetimes.rs:461:9 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -456,7 +456,7 @@ LL + fn implicit(&self) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:461:9 + --> $DIR/needless_lifetimes.rs:462:9 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -468,7 +468,7 @@ LL + fn implicit_provided(&self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:470:9 + --> $DIR/needless_lifetimes.rs:471:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -480,7 +480,7 @@ LL + fn lifetime_elsewhere(self: Box, here: &()) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:471:9 + --> $DIR/needless_lifetimes.rs:472:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -492,7 +492,7 @@ LL + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:480:5 + --> $DIR/needless_lifetimes.rs:481:5 | LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -504,7 +504,7 @@ LL + fn foo(x: &u8, y: &'_ u8) {} | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:482:5 + --> $DIR/needless_lifetimes.rs:483:5 | LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -516,7 +516,7 @@ LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:489:5 + --> $DIR/needless_lifetimes.rs:490:5 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -528,7 +528,7 @@ LL + fn one_input(x: &u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:494:5 + --> $DIR/needless_lifetimes.rs:495:5 | LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -540,7 +540,7 @@ LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:507:13 + --> $DIR/needless_lifetimes.rs:508:13 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index 65809023f8df..7803418cb047 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -1,4 +1,9 @@ -#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)] +#![allow( + dead_code, + clippy::missing_safety_doc, + clippy::extra_unused_lifetimes, + clippy::extra_unused_type_parameters +)] #![warn(clippy::new_without_default)] pub struct Foo; diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 212a69ab94e6..583dd327d6a5 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,5 +1,5 @@ error: you should consider adding a `Default` implementation for `Foo` - --> $DIR/new_without_default.rs:7:5 + --> $DIR/new_without_default.rs:12:5 | LL | / pub fn new() -> Foo { LL | | Foo @@ -17,7 +17,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Bar` - --> $DIR/new_without_default.rs:15:5 + --> $DIR/new_without_default.rs:20:5 | LL | / pub fn new() -> Self { LL | | Bar @@ -34,7 +34,7 @@ LL + } | error: you should consider adding a `Default` implementation for `LtKo<'c>` - --> $DIR/new_without_default.rs:79:5 + --> $DIR/new_without_default.rs:84:5 | LL | / pub fn new() -> LtKo<'c> { LL | | unimplemented!() @@ -51,7 +51,7 @@ LL + } | error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` - --> $DIR/new_without_default.rs:172:5 + --> $DIR/new_without_default.rs:177:5 | LL | / pub fn new() -> Self { LL | | NewNotEqualToDerive { foo: 1 } @@ -68,7 +68,7 @@ LL + } | error: you should consider adding a `Default` implementation for `FooGenerics` - --> $DIR/new_without_default.rs:180:5 + --> $DIR/new_without_default.rs:185:5 | LL | / pub fn new() -> Self { LL | | Self(Default::default()) @@ -85,7 +85,7 @@ LL + } | error: you should consider adding a `Default` implementation for `BarGenerics` - --> $DIR/new_without_default.rs:187:5 + --> $DIR/new_without_default.rs:192:5 | LL | / pub fn new() -> Self { LL | | Self(Default::default()) @@ -102,7 +102,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Foo` - --> $DIR/new_without_default.rs:198:9 + --> $DIR/new_without_default.rs:203:9 | LL | / pub fn new() -> Self { LL | | todo!() diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index ec7f8ae923a7..276266a2dd80 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::redundant_field_names)] -#![allow(clippy::no_effect, dead_code, unused_variables)] +#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] extern crate derive_new; diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 73122016cf69..f674141c138e 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::redundant_field_names)] -#![allow(clippy::no_effect, dead_code, unused_variables)] +#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] extern crate derive_new; diff --git a/tests/ui/seek_to_start_instead_of_rewind.fixed b/tests/ui/seek_to_start_instead_of_rewind.fixed index 713cff604a1d..dc24d447c607 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.fixed +++ b/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -26,7 +26,7 @@ fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { t.seek(SeekFrom::Start(0)); } @@ -38,7 +38,7 @@ fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { t.seek(SeekFrom::Start(0)); } diff --git a/tests/ui/seek_to_start_instead_of_rewind.rs b/tests/ui/seek_to_start_instead_of_rewind.rs index 467003a1a66f..4adde2c40182 100644 --- a/tests/ui/seek_to_start_instead_of_rewind.rs +++ b/tests/ui/seek_to_start_instead_of_rewind.rs @@ -26,7 +26,7 @@ fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { t.seek(SeekFrom::Start(0)); } @@ -38,7 +38,7 @@ fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { t.seek(SeekFrom::Start(0)); } diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index 2eca1f4701c9..8b4613b3f6ec 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -1,4 +1,5 @@ #![deny(clippy::type_repetition_in_bounds)] +#![allow(clippy::extra_unused_type_parameters)] use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 70d700c1cc46..a90df03c04ff 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:8:5 + --> $DIR/type_repetition_in_bounds.rs:9:5 | LL | T: Clone, | ^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:25:5 + --> $DIR/type_repetition_in_bounds.rs:26:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | Self: Copy + Default + Ord, = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:85:5 + --> $DIR/type_repetition_in_bounds.rs:86:5 | LL | T: Clone, | ^^^^^^^^ @@ -28,7 +28,7 @@ LL | T: Clone, = help: consider combining the bounds: `T: ?Sized + Clone` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:90:5 + --> $DIR/type_repetition_in_bounds.rs:91:5 | LL | T: ?Sized, | ^^^^^^^^^